一个关于JavaScript中作用域链的问题,prototype 和 __proto__ 的区别?

请看代码

代码一

<script type="text/javascript">

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.__proto__ = animal;
var tidy = new dog();

console.log(dog.price) //2000
console.log(tidy.price) // undefined

</script>

代码二

<script type="">

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.prototype = animal;
var tidy = new dog();

console.log(dog.price) //undefined
console.log(tidy.price) // 2000

</script>

为什么结果是相反的?

JavaScript中基于原型链的继承是怎么样的?

阅读 6.7k
5 个回答

__proto__ 是实例(如 tidy)访问原型对象,同时它是非标准的,ES5 标准方法是 Object.getPrototypeOf()
prototype 是构造函数(如 Dog) 访问原型对象

可以这样验证

function Dog() {}
var tidy = new Dog();

// 它们引用的是同一个内存地址,即它们的原型。
tidy.__proto__ === Dog.prototype

但是到这里并没有体现基于原型链的继承

function Dog() {}
function Animal() {}

// 实现原型链继承
Dog.prototype.__proto__ = Animal.prototype;

var tidy = new Dog();

// tidy 的原型指向 Dog.prototype
tidy.__proto__ === Dog.prototype

// 因为 Dog.prototype.__proto__ === Animal.prototype
// tidy 的原型的原型(形成原型链)指向 Animal.prototype
tidy.__proto__.__proto__ === Animal.prototype

// 原型链的终端是 Object.prototype
tidy.__proto__.__proto__.__proto__ === Object.prototype

// 再往前就是 null 了
tidy.__proto__.__proto__.__proto__.__proto__ === null

又有人问这个问题了,__proto__本质上其实是标准里规定的[[prototype]]属性,原本是不可用 js 访问的,后来(据说标准里又规定可以)firefox 和 chrome 中把这个属性命名为__proto__。后来ES又添加了函数getPrototypeof,这样就可以通过这个函数来访问这个属性,所以这个__proto__现在不是标准的一部分。

然后,再说new的创建过程,当执行new func()的时候,执行过程如下:
1、首先创建一个新的对象,比如叫 obj
2、obj.[[prototype]] = func.prototype;
3、令this=obj,执行函数 func;
4、如果 func 的返回值是一个对象,则 new 的返回值就是这个对象,否则返回值是 obj

当 __读取__(注意是读取)对象的属性的时候,返回值是:

if(obj.prop) 
    return obj.prop
else if(obj.[[prototype]].prop) 
    return obj.[[prototype]].prop;
else if(obj.[[prototype]].[[prototype]].prop) 
    return obj.[[prototype]].[[prototype]].prop;
//......一直这样找下去
else { return undefined;}

如下图:

var Fish = new Function(){} 
//等价于 
var Fish = function(){}

图片描述

根据上面大神的解释,我总结了一下,作用域链的指向如下

以下代码可以直接运行,大家可以试一下

代码一

<script type="text/javascript">

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.__proto__ = animal;
var tidy = new dog();

console.log(dog.__proto__ === animal) //true
console.log(dog.__proto__.__proto__ === animal.__proto__) //true
console.log(dog.__proto__.__proto__.__proto__  === Object.prototype) //true
console.log(dog.__proto__.__proto__.__proto__.__proto__  === null) //true

console.log(tidy.__proto__  === dog.prototype) //true
console.log(tidy.__proto__.__proto__ === dog.prototype.__proto__) //true
console.log(tidy.__proto__.__proto__ === Object.prototype) //true
console.log(tidy.__proto__.__proto__.__proto__ === null) //true

console.log(dog.price) //2000
console.log(tidy.price) // undefined

</script>

代码二

<script type="text/javascript">

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.prototype = animal;

var tidy = new dog();

console.log(tidy.__proto__  === dog.prototype) //true
console.log(tidy.__proto__.__proto__  === animal.__proto__) //true
console.log(tidy.__proto__.__proto__.__proto__ === Object.prototype) //true
console.log(tidy.__proto__.__proto__.__proto__.__proto__ === null) //true

console.log(dog.__proto__  === Function.prototype) //true
console.log(dog.__proto__.__proto__  === Object.prototype) //true
console.log(dog.__proto__.__proto__.__proto__  === null) //true

console.log(dog.price) //undefined
console.log(tidy.price) // 2000

</script>

tidy是对象,而dog是function。可以这么理解,prototye指定类型(函数)基类,而__proto__获得对象的原型链

function 拾人牙慧(){
//我觉得重点在于理解下面这段语句:
var tidy = new dog();
//即
tidy.__proto__ === dog.prototype;
}

另外dog.prototype = animal; 是不会影响原dog()函数中的属性。
而dog.__proto__指向了animal,所以dog.price继承了animal.price
不知道理解是否得当,有错误请指出。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏