/** * 关于 new 关键字的一些事 * * 当通过 new 来实例化一个构造函数时: * new ConstructorFunction(arg1, arg2); * * 它做了四件事: * 1. 创建一个新对象,仅仅是一个简单的对象 * 2. 将对象的原型链 [[prototype]] 指向构造函数 prototype 属性上 * 3. 使用新创建的对象(的作用域)来执行 ConstructorFunction * 4. 返回新创建的对象,除非构造方法返回的是一个原始值,那样的话就返回原始值 * * 做好这些事以后,如果向新建的对象请求一个不存在的属性,则将会检查其原型链 [[prototype]] 上的对象 * Functions, in addition to the hidden [[prototype]] property, also have a property called prototype, and it is this that you can access, and modify, to provide inherited properties and methods for the objects you make. * * 整个过程中,最难的部分就是第二步。所有的对象(包括函数)都有一个内置属性叫做原型链 [[prototype]] * 它只有在对象创建的时候才会进行设置,即当通过 new、Object.create,或者一些字面语句(方法依赖于 Function.prototype,数字依赖于 Number.prototype 等) * 原型链可以通过 Object.getPrototypeOf(someObject) 或者 __proto__ 或者 this.constructor.prototype 获取到 * * @相关资料: * http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript * http://zeekat.nl/articles/constructors-considered-mildly-confusing.html * https://css-tricks.com/understanding-javascript-constructors/ * https://john-dugan.com/object-oriented-javascript-pattern-comparison/ */ // 当我们这样时 function Foo() { this.kind = 'foo'; } var foo = new Foo(); foo.kind; //=> ‘foo’ // 在这背后其实有一系列的操作 function Foo() { // 实际上是不合法的,这样做只是为了讲解 var this = {}; // Step 1 this.__proto__ = Foo.prototype; // Step 2 this.kind = 'foo'; // Step 3 return this; // Step 4 } // 栗子 ObjMaker = function() { this.a = 'first'; }; // ObjMaker 仅仅是个函数,没啥特别的 ObjMaker.prototype.b = 'second'; // 跟其他函数一样,ObjMaker 有一个可以获取并改变的原型 prototype 属性 // 但也与其他对象一样,ObjMaker 还有一个我们不能获取/改变的 [[prototype]] 原型链属性 obj1 = new ObjMaker(); // 创建一个名为 obj1 的对象,一开始 obj1 和 {} 一样 // 然后 obj1 的 [[prototype]] 属性设置为 ObjMaker.prototype // 注: // 即便 ObjMaker.prototype 之后指向一个新的值,obj1 的 [[prototype]] 也不会变 // 但你可以通过添加 ObjMaker.prototype 中的属性,并将其加入到 obj1 的 prototype 和 [[prototype]] 中 // ObjMaker 方法执行完成之后,obj1.a 将指向 'first' obj1.a; // first obj1.b; // obj1 中并没有叫做 b 的属性,因此 JavaScript 将会检查其原型链,即检查 ObjMaker.prototype // 而 ObjMaker.prototype 中有名为 b 的属性,所以返回 second // 这就像是类的继承,任何你通过 new ObjMaker() 创建的实例都会继承 b 属性 // 如果你想要一个子类,可以这么干: // If you want something like a subclass, then you do this: SubObjMaker = function () {}; SubObjMaker.prototype = new ObjMaker(); // 注:不赞成使用这样的方式! // 当我们使用 new 来调用时,SubObjMaker.prototype 的原型链 [[prototype]] 属性指向 ObjMaker.prototype // 而更好的做法则是使用 ECMAScript 5 中的 Object.create() 方法: // SubObjMaker.prototype = Object.create(ObjMaker.prototype); SubObjMaker.prototype.c = 'third'; obj2 = new SubObjMaker(); // obj2 的 [[prototype]] 属性指向 SubObjMaker.prototype // 但请记住,SubObjMaker.prototype 的 [[prototype]] 指向 ObjMaker.prototype // obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype obj2.c; // returns 'third', from SubObjMaker.prototype obj2.b; // returns 'second', from ObjMaker.prototype obj2.a; // returns 'first', from SubObjMaker.prototype // 因为 SubObjMaker.prototype 是通过 ObjMaker 创建的,已经拥有了 a 属性