一个关于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中基于原型链的继承是怎么样的?

JavaScript javascript面向对象 prototype

害羞的膀胱 9 years, 5 months ago

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

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

lzcllyt answered 9 years, 5 months ago

__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

暁美吼姆辣 answered 9 years, 5 months ago

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

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

代码一


 <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>

litp317 answered 9 years, 5 months ago

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

雨夜的憔悴 answered 9 years, 5 months ago

又有人问这个问题了, __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(){}

图片描述

CPPoE answered 9 years, 5 months ago

prototype 是作用在构造方法上的,它会影响由此构造方法所创建的所有对象的原型,但它不会影响构造方法本身(构造方法本身是一个function对象)的原型。
__proto__ 是一个非标准属性,用来表示该对象当前的原型。


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

这两句代码的结果是“ tidy 对象的原型是 animal ”,也就是:
tidy.__proto__ === dog.prototype === animal
但是
dog.prototype != dog.__proto__

不知道这么说能理解吗?

harrydy answered 9 years, 5 months ago

Your Answer