最近几天在看ES6的东东,看到了对象的扩展运算赋值解构部分,对被扩展操作符应用的赋值解构变量与对象实际属性解构时候的对应关系产生了一点疑惑,所以着手研究了一下,写一写心得,防止之后再忘记或者混淆可以再回头来看看这篇文章 ,看下面例子:
const o = Object.create({ x: 1, y: 2 });
o.z = 3;
let { x, ...{ y, z } } = o;
x // 1
y // undefined
z // 3
上面代码中,变量x是单纯的解构赋值,所以可以读取对象o继承的属性;变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值。
根据上面这段代码,我查找了Object.create()
与new
操作符对于原型继承方面的知识。
这两个知识点涉及到__proto__
与prototype
,可以点击下面链接了解一下。
Javascript原型与原型链的理解
Object.create()
我们先来看一下Object.create()
在MDN上英文原话:
The Object.create() method creates a new object with the specified prototype object and properties.
- 语法
Object.create(proto[, propertiesObject])
- 参数描述
proto
The object which should be the prototype of the newly-created object
propertiesObject
Optional. If specified and not
undefined
, an object whose enumerable own properties (that is, those properties defined upon itself and not enumerable properties along its prototype chain) specify property descriptors to be added to the newly-created object, with the corresponding property names. These properties correspond to the second argument ofObject.defineProperties()
- 返回值
A new object with the specified prototype object and properties.
看着英文描述我们来模拟一下Object.create()
在执行过程中做了哪些操作呢?
①创建一个空对象{}
②指定空对象的原型(__proto__
)为Object.create()
的参数({}.__proto__ = proto
)
③返回这个对象
Object.create()的第一个参数可以是对象也可以是函数,返回值的类型实际上都是Object。
var Base = function () {}
var o2 = Object.create(Base);
//console info
Base => //ƒ () {}
o2.__proto__ => // ƒ () {}
o2.prototype === Base.prototype => //true
var obj = {s:1,b:2};
var ty = Object.create(obj)
//console info
obj => // {s: 1, b: 2}
ty.__proto__ => // {s: 1, b: 2}
obj 与 ty 都是Object对象 没有prototype属性
new操作符
MDN对于 new操作符是这样解释的:
The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
- 语法
new constructor[([arguments])]
- 参数描述
constructor
A class or function that specifies the type of the object instance.
arguments
A list of values that the constructor will be called with.
接下来我们分析一下var o = new f();
在执行过程中做了什么:
①创建一个空对象{}
②让空对象的_proto_
(IE没有该属性)成员指向了构造函数的prototype
成员对象
③使用 apply/call
调用构造器函数,属性和方法被添加到 this
引用的对象中
④如果构造函数中没有返回其它对象,那么返回 this
,即创建的这个的新对象,否则,返回构造函数中返回的对象
接下来我们模拟一下 new 的过程:
function _new() {
let obj= {}; // 创建的新对象
// 第一个参数是构造函数
let [constructor, ...args] = [...arguments];
// 执行 [[原型]] 连接 ;实际上就是生产了一个新的上下文
obj.__proto__ = constructor.prototype;
// 使用apply在obj作用域中调用构造器函数,属性和方法被添加到 this 引用的对象即obj中
let result = constructor.apply(obj, args);
if (result && (typeof (result) == "object" || typeof (result) == "function")) {
// 如果构造函数执行的结果返回的是一个对象,那么返回这个对象
return result;
}
// 如果构造函数返回的不是一个对象,返回创建的新对象
return obj;
}
来实践一下:
var Base = function () {}
var o1 = _new();
//console info
Base.prototype === o1.__proto__ => //true
下面我们结合实例来加深一下理解:
代码片段一
function Person(age){
this.age= age;
console.log(this);
return {age:age};//返回对象
}
Person.prototype.index = 1
var person1 = new Person(20); // 此处相当于var person1=_new(Person, 20)
var person2 = Person(18);
console.log(person1);
console.log(person2);
console.log('p1.index=', person1.index)
console.log('p2.index=', person2.index)
-
obj._proto_ = constructor.prototype
,即obj._proto_ = Person.prototype
,将obj
的_proto_
(隐式原型)指向Person
的原型对象,此时obj
的原型链为:
obj => Person.prototype => Object.prototype => null
因此 执行到let result = constructor.apply(target, args)
这句代码时,在obj
作用域中调用Person
函数,打印出Person {age: 20}
因为Person
函数返回的是一个对象,所以console.log(person1)
打印出的就是返回的这个对象:{age: 20}
,person1.index
自然就为undefined
而var person2 = Person(18)
,只是简单的调用了Person
函数,在Window
全局中执行,所以打印出Window
对象,执行console.log(person2)
打印出:{age: 18}
,person2.index
为undefined
代码片段二
function Person(age){
this.age= age;
console.log(this);
// return {age:age};//返回对象
}
Person.prototype.index = 1
var person1 = new Person(20); // 相当于var person1 = _new (Person, 20);
var person2 = Person(18);
console.log(person1);
console.log(person2);
console.log('p1.index=', person1.index)
console.log('p2.index=', person2.index)
- 代码片段二和代码片段一的区别在于, 片段二中构造函数
Person
没有返回值(返回的不是一个对象,还可以通过直接返回age验证下效果),所以person1
接收到的实际上是_new
新创建的对象obj
,即Person {age: 20}
,person1.index
则相当于obj.index
属性时,它会先找自身的index
属性,如果找不到,则会顺着原型链向上找,这时会找到People.prototype.index
,person1.index
的结果是1。
由于Person
没有返回值,所以console.log(person2)
结果为undefined
,进而,打印person2.index
时会报错。
new 操作符只能对构造函数进行创建实例操作
以上new的部分借鉴了JS new 创建对象原理