js的new到底干了什么,如何手写一个new
let arr = new Array() 到底内部发生了什么? js 中的实例是如何创建出来的?它和 Object.create() 区别是什么?
原型链说起
每个实例对象(object)都有一个私有属性(称之为
__proto__
)指向它的构造函数的原型对象(prototype
)。该原型对象也有一个自己的原型对象(__proto__
),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
javascript
let f = function () {
this.a = 1;
this.b = 2;
};
f.prototype.b = 3;
f.prototype.c = 4;
let o = new f();
console.log(o.__proto__ === f.prototype); // true
// 私有属性 __proto__ 指向了构造函数的原型对象
console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为 1
console.log(o.b);
// b是o的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"
console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看它的原型上有没有
// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4
console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined
new 内部发生了什么?原型链如何指向?
javascript
let f = function () {
this.a = 1;
this.b = 2;
};
f.prototype.b = 3;
f.prototype.c = 4;
let o = new f();
console.log(o);
// o 为新创建的空对象,它的 __proto__ 指向 f.prototype
// o.__proto__ === f.prototype // true
new 和 Object.create() 有什么区别?
javascript
let fn = function () {
this.a = 1;
this.f = function () {
console.log(f);
};
};
let createFn = Object.create(fn);
// createFn 的原型 指向 传入的 fn 的地址
console.log(createFn.__proto__);
// VM513:1 ƒ () {
// this.a = 1;
// this.f = function () {
// console.log(f);
// };
// }
console.log(createFn.__proto__ === fn); // true
console.log(createFn.constructor === fn.constructor); // true
console.log(createFn.constructor === fn.prototype.constructor); // false
let newFn = new fn();
console.log(newFn.__proto__ === fn.prototype); // true
console.log(newFn.constructor === fn.constructor); // false
console.log(newFn.constructor === fn.prototype.constructor); // true
对比 create 和 new 得知
- create
createFn.__proto__
=== fn
createFn.constructor
=== fn.constructor
- new
newFn.__proto__
=== fn.prototype
newFn.constructor
=== fn.prototype.constructor
也就是 create 的对象他的原型链都是指向 fn 本体的。而 new 的对象 都是指向 fn.prototype
fn.constructor 和 fn.prototype.constructor 有什么关系
javascript
// 还是这个栗子
let fn = function () {
this.a = 1;
this.f = function () {
console.log(f);
};
};
console.log(fn.prototype.constructor === fn); // true
console.log(fn.constructor === Function); // true
由上可知
- 一个对象的 fn.constructor 是指向其父类本体的
- 一个对象的 fn.prototype.constructor 是指向本体的
一张图就能明白:
手写一个 new 吧
- new 返回一个新对象
- 该对象是执行了 目标类 constructor 之后的结果
- 该对象的
__propto__
指向 目标类的 prototype
js
const myNew = function (context, ...args) {
let obj = {};
obj.__proto__ = context.prototype;
context.apply(obj, args);
return obj;
};
function person(name, age) {
this.name = name;
this.age = age;
}
let p = myNew(person, '布兰', 12);
console.log(p); // { name: '布兰', age: 12 }
再手写一个 Object.create 吧
__proto__
指向目标对象即可
js
const person = {
isHuman: false,
printIntroduction: function () {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
},
};
Object.prototype.MyCreate = function (context) {
let obj = {};
obj.__proto__ = context;
return obj;
};
const me = Object.create(person);
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
console.log(me);
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
const me2 = Object.MyCreate(person);
me2.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me2.isHuman = true; // inherited properties can be overwritten
console.log(me);
me2.printIntroduction();