关于 js 原型链继承的问题(头大),请求支援

2020-07-06 22:29:02 +08:00
 rzWack

片段 1: let Foo = function Foo(){}; Foo.prototype= {x:1};
let FooSon = new Foo(); console.log(FooSon.x); //输出:1 Foo.prototype.x =2; console.log(FooSon.x); //输出:2

片段 2: let Con = function Con(){} Con.prototype.x =2; let ConSon = new Con(); console.log(ConSon.x); //输出:2 Con.prototype= {x:1};
console.log(ConSon.x); //输出:2

let QonSon = new Qon(); console.log(QonSon.x); //输出:1 =>引用了最新原型

问题是看不懂为什么先 prototyp={},再 prototype.x 方式时候后面的实例能够继承到最新的原型,反过来就不可以?这里面牵涉到了什么高深知识点,万能的 v 友久久我。

3525 次点击
所在节点    JavaScript
33 条回复
rpxwa
2020-07-06 22:40:08 +08:00
可以看出 prototype 本质上是一个指针。

我给了你一张纸条,纸条上写着我家的地址,这个地址就叫指针。

当我把我家玻璃砸碎,你通过这个地址来到我家,你就能看到玻璃已被砸碎。

然后我把你手里的纸条撕碎,给了你一张新纸条,纸条上写着我刚买的毛坯房地址。

现在我把我原来家的玻璃修好。

你顺着纸条找过来,目光所及没有修好的玻璃,只有空空如也的房子。
JerryY
2020-07-06 22:45:45 +08:00
建议先看看红宝书再来思考这个问题。这里给一点参考,prototype={}已经改写了原型对象了,这时候再 prototype.x 是在这个改写后的对象上的修改,这里不是你说的`继承到最新的原型`;反过来的话,就更好理解了,因为你先 prototype.x 赋值,再改掉这个 prototype 的整个对象,这时候原型链已经“丢失”了。
ChanKc
2020-07-06 22:51:04 +08:00
想了一下
原型链其实继承的是对象
Foo.prototype= {x:1}; 这里是表示 new Foo 创建的对象要继承于匿名的{x:1}对象
Foo.prototype= {x:2}; 这时候就继承于另外一个新创建的匿名对象{x:2}了,不过因为原来的{x:1}还能通过你之前 new 的 Foo 的原型来获取,所以不会被回收
ChanKc
2020-07-06 22:55:40 +08:00
@ChanKc
修正
原来的{x:1}还能通过 Object.getPrototypeOf 获取
Kaciras
2020-07-06 23:00:41 +08:00
不知道你能不能理解这个最基本的引用问题:

let A = { x: 1 };
let B = A;
A.x = 2;

请问现在 B.x = ?

let A = { x: 2 };
let B = A;
A = { x: 1 };

请问现在 B.x = ?

你的例子就是上面的复杂版,没有高深的知识点,只是“原型链”三个字把你吓着了。

我是这么理解原型链的,就两条:
1.实例化时,自动给实例添加一个__proto__的属性,其值为类(或函数)的 prototype 属性
2.从对象查找属性时,如果找不到则自动往__proto__上递归查找

let ConSon = new Con(); // ConSon.__proto__ = Con.prototype
Con.prototype= {x:1}; // Con.prototype 重新赋值,但是 ConSon.__proto__ 仍指向旧的值
ChanKc
2020-07-06 23:02:00 +08:00
另外本质上,new 可以大致等同于以下操作
let foo = Object.create(Foo.prototype);
Foo.call(foo)

这里就只是用了一些 Foo 的原型链指向的一个对象,然后再拿 Foo 作为普通函数调用一下。可以说构造完的对象和 Foo 这个函数没什么关系了
rzWack
2020-07-06 23:03:58 +08:00
@JerryY 嗯嗯,这个我理解了。但是这里还有个奇怪的问题,在先 prototype.x 赋值,再通过 prototype={..} 覆盖掉当前实例的原型后,再使用 prototype.x 赋值。此时输出的值仍然还是第一次 prototype.x 的值。这里是不符合前面 “ 再 prototype.x 是在这个改写后的对象上的修改 ”的逻辑的呀
rzWack
2020-07-06 23:15:59 +08:00
@Kaciras 第一个是引用,值跟着变;第二个不太懂,用浏览器输出了一下 B.x=2,猜想 A={..}的时候重写地址发生了改变,B 还是指向原来 A 的地址,所以还是 2
ChanKc
2020-07-06 23:19:40 +08:00
@rzWack 索性这样,单纯只考虑原型链的话
我把 foo = new Foo()替换成

foo = {}
Object.setPrototypeOf(foo, Foo.prototype)
Foo.call(foo)

接下来我把 Foo.prototype 换个写法

let obj = {x:1}
Foo.prototype = obj
foo = {}
Object.setPrototypeOf(foo, Foo.prototype)
Foo.call(foo)

然后我等价代换一下

let obj = {x:1}
Foo.prototype = obj
foo = {}
Object.setPrototypeOf(foo, obj)
Foo.call(foo)

问此时 foo 这个对象和 Foo.prototype 还有什么关系
ChanKc
2020-07-06 23:20:23 +08:00
@rzWack 你的猜想是对的
hurrytospring
2020-07-06 23:21:28 +08:00
理解一下把 new 的过程实现一遍清楚了
Jirajine
2020-07-06 23:23:36 +08:00
看这个,图文并茂深入浅出
https://javascript.info/function-prototype
Kaciras
2020-07-06 23:29:42 +08:00
@rzWack 你的猜想是对的,把 A 换成 Con.prototype,B 换成 ConSon.__proto__ 就是你的问题了
shuangya
2020-07-06 23:43:58 +08:00
rzWack
2020-07-06 23:53:53 +08:00
@ChanKc 这里只看懂到
Object.setPrototypeOf(foo, obj) => foo.prototype=obj 。Foo.call(foo)这一步不懂,大佬能给解释一下不😂
Pyrex23
2020-07-07 00:38:25 +08:00
@rzWack 调用了构造函数 Foo,并把 this 指向实例 foo 。这一步是在初始化属性
cs419
2020-07-07 07:05:19 +08:00
let Foo = function Foo(){};
Foo.prototype= {x:1};

let s01 = new Foo();
s01.__proto__.y=2;
console.log(s01.__proto__ == Foo.prototype);
let s02 = new Foo();
console.log(s02.y);

Foo.prototype = {z:3};
console.log(s01.__proto__ == s02.__proto__);
console.log(s01.__proto__ == Foo.prototype);
Mutoo
2020-07-07 07:53:58 +08:00
分享一下我的笔记:JS 原型拓扑
https://blog.mutoo.im/2015/01/topology-of-javascript-prototype/

画重点:
当 let foo = new Foo() 的时候,伪代码相当于
let foo = {__proto: Foo.prototype };
之后修改 Foo.prototype 指向另一个 {} 时,不影响已经创建的 foo 的 __proto 指向原来的 prototype
ChanKc
2020-07-07 08:22:43 +08:00
@rzWack Foo.call(foo)就是“让 Foo 函数中的 this 指向 foo,并在这个情况下调用 Foo 函数”
ChanKc
2020-07-07 08:54:02 +08:00
@rzWack setPrototypeOf 等于楼里其他人说的调用__proto__的 setter 。我不用__proto__是因为在最新的 ecmascript 标准里不推荐用

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/687713

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX