共计 2435 个字符,预计需要花费 7 分钟才能阅读完成。
1.super 关键字
(1)咱们晓得,this 关键字总是指向函数所在的以后对象,ES6 又新增了另一个相似的关键字 super,指向以后对象的原型对象。
const proto = {foo: 'hello'};
const obj = {
foo: 'world',
find() {return super.foo; // 这里的 super 指向的是 obj 对象原型}
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
Object.setPrototypeOf(_obj, prototype_):该函数的作用是 为一个对象设置原型。
下面代码中,对象 obj.find()办法之中,通过 super.foo 援用了 原型对象 proto 的 foo 属性。
(2)留神,super 关键字示意原型对象时,只能用在对象的办法之中,用在其余中央都会报错。请看上面的例子:
// 报错
const obj = {foo: super.foo}
// 报错
const obj = {foo: () => super.foo
}
// 报错
const obj = {foo: function () {return super.foo}
}
下面三种 super 的用法都会报错,因为对于 JavaScript 引擎来说,这里的 super 都没有用在对象的办法之中。 第一种写法是 super 用在属性外面,第二种和第三种写法是 super 用在一个函数外面,而后赋值给 foo 属性。目前,只有对象办法的简写法能够让 JavaScript 引擎确认,定义的是对象的办法。
在这里,可能第二种写法和第三种写法为什么谬误会让人混同,其实是这样的,在对象中,是有上面这种模式才会默认是对象的办法:
const obj = {foo() {}, //foo 是对象的办法
func: function foo1() {} //foo1 不是对象的办法
}
(3)JavaScript 引擎外部,super.foo 等同于 Object.getPrototypeOf(this).foo(属性)或 Object.getPrototypeOf(this).foo.call(this)(办法)。
(4)如果在一个对象的办法中应用了 super.protoFunc(),而且在该对象的原型对象中的 protoFunc 办法中应用了 this,该 this 不是指向原型对象,而是指向实例对象。
const proto = {
x: 'hello',
foo() {console.log(this.x); // 这里的 this 仍旧是绑定了 obj 对象
},
};
const obj = {
x: 'world',
foo() {super.foo();
}
}
Object.setPrototypeOf(obj, proto);
obj.foo() // "world"
下面代码中,super.foo 指向原型对象 proto 的 foo 办法,然而绑定的 this 却还是以后对象 obj,因而输入的就是 world。起因也很简略,因为一般函数的 this 是指向执行作用域的,恰好 this 执行时的作用域是在 obj 对象中,所以这里的 this 是指向了 obj 对象。
2. 对象的扩大运算符
(1)对象的解构赋值用于从一个对象取值,相当于将指标对象本身的所有可遍历的(enumerable)、但尚未被读取的属性,调配到指定的对象下面。所有的键和它们的值,都会拷贝到新对象下面。
let {x, y, ...z} = {x: 1, y: 2, a: 3, b: 4};
x // 1
y // 2
z // {a: 3, b: 4}
下面代码中,变量 z 是解构赋值所在的对象 。它获取等号左边的 所有尚未读取的键(a 和 b),将它们连同值一起拷贝过去。
在这里,解构要留神几点:
- 因为解构赋值 要求等号左边是一个对象 ,所以 如果等号左边是 undefined 或 null,就会报错,因为它们无奈转为对象。
let {...z} = null; // 运行时谬误
let {...z} = undefined; // 运行时谬误
- 解构赋值必须是最初一个参数,否则会报错。
let {...x, y, z} = someObject; // 句法谬误
let {x, ...y, ...z} = someObject; // 句法谬误
- 解构赋值的拷贝是 浅拷贝 ,即如果一个键的值是 复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的援用,而不是这个值的正本(意思就是指其实两者是指向同一块地址空间)。
let obj = {a: { b: 1} };
let {...x} = obj;
obj.a.b = 2;
x.a.b // 2
留神,这里 x.a.b 不是 1 而是 2,因为 x 其实就是对 obj 的另一个援用,并没有开拓新的地址空间。
- 扩大运算符的解构赋值,不能复制继承自原型对象的属性
let o1 = {a: 1};
let o2 = {b: 2};
o2.__proto__ = o1;
let {...o3} = o2;
o3 // {b: 2}
o3.a // undefined
下面代码中,对象 o3 复制了 o2,然而只复制了 o2 本身的属性,没有复制它的原型对象 o1 的属性。
- ES6 规定,变量申明语句之中,如果应用解构赋值,扩大运算符前面必须是一个变量名,而不能是一个解构赋值表达式,所以下面代码引入了两头变量 newObj,如果写成上面这样会报错。
let {x, ...{ y, z} } = o;
// SyntaxError: ... must be followed by an identifier in declaration contexts
- 解构赋值的一个用途,是 扩大某个函数的参数,引入其余操作。
function baseFunction({a, b}) {// ...}
function wrapperFunction({x, y, ...restConfig}) {
// 应用 x 和 y 参数进行操作
// 其余参数传给原始函数
return baseFunction(restConfig);
}
下面代码中,原始函数 baseFunction 承受 a 和 b 作为参数,函数 wrapperFunction 在 baseFunction 的根底上进行了扩大,可能承受多余的参数,并且保留原始函数的行为。