new 操作符
先让我们看一段简单的代码:
function Test() {
this.a = "a";
}
Test.prototype.getThis = function() {
console.log(this);
};
const test = new Test();
test.getThis(); // { a: "a", __proto__: Test.prototype }
new
操作符做了什么?
引用 MDN 的说法:
-
创建一个空白且简单的对象
通过
new Object(null)
创建一个空白对象{}
-
链接该对象到另一个对象
为步骤 1 创建的对象添加
__proto__
属性指向函数原型 (Test.prototype
).
得到{ __proto__: Test.prototype }
-
将步骤 1 创建的对象作为
this
即
this = { __proto__: Test.prototype }
执行
Test
的 constructor 函数, 最终this = { a: 'a', __proto__: Test.prototype }
-
如果该函数没有返回 对象, 则返回
this
, 否则返回 对象
prototype
prototype 中文名叫做原型
重点:只有函数拥有原型
函数原型的好处: 当通过 new
操作符创建新对象时, 如果没有原型, 那么我们把所有属性都挂载到实例上, 每一个实例都这样做是浪费内存的
而有了原型, 我们就可以通过引用原型的方式共享属性(共享的属性一般为函数方法)
通过前面我们知道 test = { a: 'a', __proto__: Test.prototype }
test
中虽然没有 getThis
方法, 但是仍然可以调用 getThis
, 这里面就是原型链起作用了
__proto__
Chrome 用 __proto__
实现原型链, 所有对象均存在 __proto__
属性(null 不是对象, null 没有 __proto__
属性)
当读取实例的属性时, 如果找不到, 就会查找与对象关联的原型中的属性, 如果还查不到, 就去找原型的原型, 一直找到最顶层为止 JavaScript 深入之从原型到原型链
分析上面的例子, 因为 test
中没有 getThis
方法, 所以去查找 test.__proto__.getThis
, 即 Test.prototype.getThis
那如果我们调用 test.hasOwnProperty
又是怎样的呢?
首先, 在 const test = new Test()
过程中, 创建以下关系:
之前提到, 所有对象均存在 __proto__
属性, 所以 Test.prototype
也存在 __proto__
属性, 那么这个值是什么?
答案很简单, Test.prototype
是个对象, 所以 Test.prototype.__proto__ = Object.prototype
.
更新关系图如下:
而 Object.__proto__ = null
, 所以完整原型链如下:
补充
函数原型创建时有 constructor
属性指向函数本身, 即:
function Test() {}
Test.prototype.constructor === Test; // true
JS 里面的 Class
本身就是函数的语法糖, 不再需要修改函数的 prototype
属性来共享方法, extends
实际上就是修改了原型的 __proto__
属性
class Test1 {
a = "a";
constructor() {
this.b = "b";
}
getC() {
console.log(this.c);
}
}
class Test2 extends Test1 {
constructor() {
super();
this.c = "c";
}
getAB() {
console.log(this.a, this.b);
}
}
const test = new Test2();
test.getAB();
test.getC();
写成函数形式:
function Test1() {
this.a = "a";
this.b = "b";
}
Test1.prototype.getC = function() {
console.log(this.c);
};
function Test2() {
Test1.call(this);
this.c = "c";
}
Test2.prototype.getAB = function() {
console.log(this.a, this.b);
};
Reflect.setPrototypeOf(Test2.prototype, Test1.prototype);
const test = new Test2();
test.getAB();
test.getC();