Object.create()与new操作符

最近几天在看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 of Object.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)
Object.create()与new操作符_第1张图片
片段一输出结果
  • 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.indexundefined

代码片段二

    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)
Object.create()与new操作符_第2张图片
片段二输出结果
  • 代码片段二和代码片段一的区别在于, 片段二中构造函数Person没有返回值(返回的不是一个对象,还可以通过直接返回age验证下效果),所以person1接收到的实际上是_new新创建的对象obj,即Person {age: 20}, person1.index则相当于obj.index属性时,它会先找自身的index属性,如果找不到,则会顺着原型链向上找,这时会找到People.prototype.indexperson1.index的结果是1
    由于Person没有返回值,所以console.log(person2)结果为undefined,进而,打印person2.index时会报错。

new 操作符只能对构造函数进行创建实例操作
以上new的部分借鉴了JS new 创建对象原理

你可能感兴趣的:(Object.create()与new操作符)