0%

学习笔记 2020 10 20

学习笔记 2020-10-20

JavaScript 高级程序设计(第4版) 阅读记录

对象、类与面向对象编程

对象迭代

  1. 其他原型语法

    可以通过使用一个包含所有属性和方法的对象字面量来重写原型。

    重写后,构造函数原型的 constructor 便不再指向构造函数。必要时可以专门设置,如果选择直接在重写原型对象时写入 constructor ,那么这个属性会被设置为可枚举的。最好选择 Object.defineProperty 来定义这个属性。

  2. 原型的动态性

    从原型上搜索值的过程是动态的。对原型做了修改,即使是已经被创建出来的实例也可以反映出这个变化。但是如果是重写了构造函数的原型对象的话,原本的实例依然指向最初的原型,而无法反映这个改写的原型。

    function Person() {}
    let friend = new Person();
    Person.prototype = {
      constructor: Person,
      name: 'Nicholas',
      age: 29,
      job: 'Software Engineer',
      sayName() {
        console.log(this.name);
      }
    };
    friend.sayName(); // Uncaught TypeError: friend.sayName is not a function

    原因是对原型做赋值操作,是直接修改了指针指向,但无法修改到已创建实例的原型指针。

  3. 原生对象原型

    所有原生引用类型都采用原型模式实现。所有原生引用类型的构造函数都在原型上定义了实例方法。

    String.prototype.startsWith = function (text) {
      return this.indexOf(text) === 0;
    };
    let msg = 'Hello world!';
    console.log(msg.startsWith('Hello')); // true

    也可以自定义原型对象上的方法。

  4. 原型的问题

    原型模式弱化了向构造函数传递初始化参数的能力,会导致所有实例都取得相同的属性值。

    原型最大的问题是它的共享特性,原型对象上存在的属性会被所有实例共享,也能被所有实例修改。而不同的实例应该有自己单独的属性副本。

继承

继承一般分为两种:接口继承和实现继承。ECMAScript 中不存在接口继承,函数没有签名。因此,ECMAScript 支持实现继承,通过原型链实现。

原型链
function SuperType() {
  this.property = true;
}
SuperType.prototype.getSuperValue = function () {
  return this.property;
};
function SubType() {
  this.subproperty = false;
}
// 继承 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
  return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true

示例中重写了 SubType 的原型,替换为了 SuperType 的实例。构成了一条原型链。

  1. 默认原型

    默认情况下,所有引用类型都继承自 Object 。任何函数的默认原型都是一个 Object 的实例。

  2. 原型与继承关系

    原型与实例的关系可以通过 instanceof 操作符确定,只要一个实例的原型链中出现过相应的构造函数,该操作符就会返回 true

    第二种方法是通过 isPrototypeOf() 方法。由原型链上的原型来调用这个方法,只要被传入的实例参数原型链中包含这个原型,就会返回 true

  3. 关于方法

    使用对象字面量的方式创建原型方法会破坏之前的原型链。

  4. 原型链的问题

    原型链的问题在于原型中包含引用值得时候,该引用值会在所有实例间共享。虽然我们原先是在对象实例上定义属性,但这个类型一旦被继承,它的实例属性就成了子类的原型属性。在这种模式下,我们不能在子类实例化的时候给父类型的构造函数传参。

盗用构造函数

为了解决上面提到的问题,引用了一种叫盗用构造函数的技术,也称对象伪装或经典继承。思路为:在子类构造函数中调用父类构造函数。

function SuperType() {
  this.colors = ['red', 'blue', 'green'];
}
function SubType() {
  // 继承 SuperType
  SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"
  1. 传递参数

    使用 call 或是 apply 的好处就在于我们可以传参,就实现了子类构造函数给父类构造函数传参。

  2. 盗用构造函数的问题

    问题在于,必须在构造函数中定义方法,函数不能被重用。

组合继承

组合继承综合了原型链和盗用构造函数,思路为使用原型链继承原型上的属性和方法,通过盗用构造函数继承实例属性。

function SuperType(name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
  console.log(this.name);
};
function SubType(name, age) {
  // 继承属性
  SuperType.call(this, name);
  this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function () {
  console.log(this.age);
};
let instance1 = new SubType('Nicholas', 29);
instance1.colors.push('black');
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";
instance1.sayAge(); // 29
let instance2 = new SubType('Greg', 27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27

这样,各个实例有自己的实例属性,也可以共享父类的方法。

MDN学习记录

<video> 与 <audio>

在网页中添加视频或音频可以用 html5 新增的标签,可以直接指定单独的 videoaudio 标签使用。

<video src="./friday.mp4" controls></video>

src 属性指定视频源, controls 指定是否展示控制条。

但因为视频格式多种多样,不同浏览器采用的编码器不一样,所以我们要尽可能兼容各种格式的视频。可以在 video 标签内部放入 source 标签来指定不同的视频源。

<video controls>
  <source src="./friday.mp4" type="video/mp4">
  <p>你的浏览器不支持 HTML5 视频。可点击<a href="friday.mp4">此链接</a>观看</p>
</video>

当浏览器解析时会寻找匹配的视频格式来解码。同时可以在内部放入一个后备内容用于视频不可用时的展示。

video 标签的其余属性:

  • width height

    可以手动设定视频的宽高。

  • autoplay

    视频加载完后自动播放。

  • loop

    视频循环播放。

  • muted

    视频默认关闭声音。

  • poster

    指定视频播放前的视频封面。

  • preload

    用于缓冲较大的文件:

    • none 不缓冲。
    • auto 页面加载后缓冲媒体文件。
    • metadata 仅缓冲文件的元数据。

audio 标签与 video 类似,但没有 width/height 属性,不支持 poster 属性。

两个标签对应的 dom 元素都可以调用 load() 方法来重置播放。

可以通过监听 addtrack 事件来监听音频轨道的增删。

video 标签支持显示音轨文本,配置一个音轨文本的 vtt 文件。

WEBVTT

00:00:00.000 --> 00:00:00.999  line:80%
Hildy!

00:00:01.000 --> 00:00:01.499 line:80%
How are you?

00:00:01.500 --> 00:00:02.999 line:80%
Tell me, is the lord of the universe in?

00:00:03.000 --> 00:00:04.299 line:80%
Yes, he's in - in a bad humor

00:00:04.300 --> 00:00:06.000 line:80%
Somebody must've stolen the crown jewels

然后在 video 中配置 track 标签指明音轨文本来源。

<video controls width="300" height="500" muted>
  <source src="./friday.mp4" type="video/mp4">
  <track default kind="captions" srclang="en" src="friday.vtt" />
  <p>你的浏览器不支持 HTML5 视频。可点击<a href="rabbit320.mp4">此链接</a>观看</p>
</video>

视频预览

<picture>

picture 标签可用于响应式图片。但 img 本身也支持这个功能,先看一下 img 的例子。

<img srcset="elva-480w.jpg 480w,
             elva-800w.jpg 800w"
     sizes="(max-width: 320px) 280px,
            (max-width: 480px) 440px,
            800px"
     src="elva-800w.jpg">

srcset 指定了图像集以及每张图片的大小,此处的 480w 指的是图片真实宽度为 480px

sizes 指定了一组媒体条件,当某个媒体条件为真时,它对应的宽度会赋给该 img 标签。

在多次测试这个功能后,我发现 img 的标签的响应式选择图片仅在初次加载时执行。

也就是说,浏览器只会在第一次加载时来根据设备宽度选择图片,在此之后无论设备宽度如何变化,它只会根据我们设定好的条件来改变图片的大小而不会更换图片。

<meta name="viewport" content="width=device-width">

上述这个元数据标签指定了浏览器采用真实可视宽度来加载网页。

也可以实现相同尺寸不同分辨率的情况。

<img srcset="elva-480w.jpg,
             elva-800w.jpg 2x"
     src="elva-800w.jpg">

这个例子去掉了 sizes 。我们不修改 img 标签的大小。在不同尺寸下加载不同图片,以适应不同分辨率的设备更好的显示。

响应式切换

如果需要完成响应式切换图片的话,需要使用 picture 标签。

<picture>
  <source media="(max-width: 799px)" srcset="elva-480w.jpg">
  <source media="(min-width: 800px)" srcset="elva-800w.jpg">
  <img src="elva-800w.jpg">
</picture>

picture 标签内部使用 source 标签指定图片, media 属性设置激活的媒体条件, srcset 指定生效的图片。

最后的 img 标签作为后备内容。

Mozilla醒目页面

实现一个网页,在设备宽度不同的情况下,加载不同的图片。

设备宽度大于 600px

图片小于 480px

在设备宽度小于 480px 时,四个导航的图片加载 120px 版本。

大于 480px 时,加载 400px 版本。

宽度小于 600px 时,加载 600px 版本的小熊猫图片,否则加载 1200px 版本。

列出主要代码:

<img
  srcset="./images/firefoxlogo120.png 120w, ./images/firefoxlogo400.png 400w"
  sizes="(max-width: 480px) 120px,
         (min-width: 481px) 400px"
  src="./images/firefoxlogo400.png"
/>

<picture>
  <source srcset="./images/redpanda600.jpg" media="(max-width: 600px)" />
  <source srcset="./images/redpanda1200.jpg" media="(min-width: 601px)" />
  <img src="./images/redpanda1200.jpg" />
</picture>