Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深入原型和原型链 #39

Open
amandakelake opened this issue Mar 29, 2018 · 4 comments
Open

深入原型和原型链 #39

amandakelake opened this issue Mar 29, 2018 · 4 comments

Comments

@amandakelake
Copy link
Owner

amandakelake commented Mar 29, 2018

525140ed-2f51-44d6-93c7-e10fa7b9c4f1
这图很经典,希望先试着解读每个箭头的含义

对象都是通过函数创建的,函数是对象(剪不断,理还乱,是离愁,别是一番滋味在心头)

一、Prototype

每个函数都有这属性,有且仅有函数才拥有该属性
但有个例外:

// 用这个方法创建的函数是不具备prototype属性的
let fun = Function.prototype.bind()

Prototype属性里面默认有一个属性constructor,对应着构造函数

constructor是一个公有的而且不可枚举的属性,一旦改写了函数的Prototype,新对象就没有这个属性了
4e7d3878-1b22-44d9-a3bc-26965bbbf9e9

二、__proto__

每个对象都有一个隐藏属性__proto__,这个属性引用了创建这个对象的构造函数的原型
fn.__proto__ === Fn.prototype

__proto__ 将对象和原型连接起来组成了原型链

有三个要注意的地方

1、Object.prototype.__proto__ === null

对象的原型的__proto__属性是null

2、Object.__proto__ === Function.prototype

既然对象是函数创建的,那么对象的__proto__要指向创建它的构造函数的原型

3、Function的特殊性Function.__proto__ === Function.prototype

函数也是被Function创建的,那么函数的__proto__也应该指向Function的原型,这是一个环形结构,函数的prototype__proto__属性指向同一个对象

Function.prototypeObject.prototype 是两个特殊的对象,他们由引擎来创建

三、new

除了以上Function.prototypeObject.prototype两个特殊对象,其他对象都是通过构造器 new 出来的

先看看new的实现过程

  • 声明一个中间对象;
  • 将该中间对象的原型指向构造函数的原型;
  • 将构造函数的this,指向该中间对象;
  • 返回该中间对象,即返回实例对象。
function create() {
  // 创建一个新的对象
  let obj = new Object();
  // 取出第一个参数,该参数就是我们将会传入的构造函数
  // arguments会被shift去除第一个参数
  let Constructor = [].shift.call(arguments);
  // 将obj的原型指向构造函数,此时obj可以访问构造函数原型中的属性
  obj.__proto__ = Constructor.prototype;
  // 改变构造函数的this的指向,使其指向obj
  // 此时obj也可以访问构造函数中的属性了
  let result = Constructor.apply(obj, arguments);
  // 确保 new 出来的是个对象
  // 返回的值是什么就return什么出来
  return typeof result === 'object' ? result : obj 
}

所以平时更推荐使用字面量的方式创建对象,因为使用new Object()需要通过作用域链往上层层寻找Object

因为存在我们创建了一个同名的构造函数Object()的可能,当调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,如果在当前作用域找到了名为Object()的函数就执行,如果没找到,就继续顺着作用域链往上照,直到找到全局Object()构造函数为止

四、原型链

上面说__proto__ 将对象和原型连接起来组成了原型链

具体起来说就是:访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链

来看个例子

function Foo() {};
let f1 = new Foo();

f1.a = 1;
Foo.prototype.a = 10;
Foo.prototype.b = 20;

// 打印自身属性和原型属性
for(const item in f1) {
  console.log(item)
}

// 打印自身属性
for (const item1 in f1) {
  if (f1.hasOwnProperty(item1)) {
    console.log(item1);  
  }
}

2dc87a25-f091-429d-940c-a7b11c668f03

稍等一下,hasOwnProperty这个方法从哪找的?
f1Foo.prototype都没有啊

很好,它从Object.prototype继承而来的,由于__proto__的存在,可以查找下面这条链条
f1 => Foo.prototype => Object.prototype
最终找到了Object.prototype上定义的各种原生方法
e149e038-97b0-4d6e-9eb0-0e5cf47ab1b4

那我们平时看见的callapply,还有数组的一些方法呢?
它们分别在Function.prototypeArray.prototype上面,我们日常使用的原生方法都继承于这几个对象,可以尝试去浏览器控制台打印这两看看

console.dir(Function.prototype);
console.dir(Array.prototype)

原型、原型链我说清楚了吗?

参考资料
深入理解javascript原型和闭包(1)——一切都是对象 - 王福朋 - 博客园
深度解析原型中的各个难点 · Issue #2 · KieSun/Blog · GitHub

@243001806
Copy link

加个微信吧!

@eyea
Copy link

eyea commented Jul 30, 2019

Prototype属性里面默认有一个属性constructor,对应着构造函数

这句话读了两三遍,这样可能更好一点

构造函数的原型属性里面默认有一个constructor属性,指想构造函数

@eyea
Copy link

eyea commented Jul 30, 2019

很好,它从Object.prototype继承而来的,由于__proto__的存在,可以查找下面这条链条
f1 => Foo.prototype => Object.prototype

这个应该是

很好,它从Object.prototype继承而来的,由于__proto__的存在,可以查找下面这条链条
f1 => Foo.__proto__=> Object.prototype

@243001806
Copy link

很好,它从Object.prototype继承而来的,由于__proto__的存在,可以查找下面这条链条
f1 => Foo.prototype => Object.prototype

这个应该是

很好,它从Object.prototype继承而来的,由于__proto__的存在,可以查找下面这条链条
f1 => Foo.__proto__=> Object.prototype

能加一下你微信吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants