JS 入门小白求助:
为什么我在 Student 里面写的 helloStudent 函数显示在 Object 类里面,而在 PrimaryStudent 里面写的 helloPrimaryStudent 函数显示是在 Student 类里面?
1
seakingii 2021-10-04 21:17:52 +08:00 via Android 1
没有错,是你看错了
|
2
demonzoo 2021-10-04 21:27:30 +08:00
console 里面那个 prototype 看到的是父级的属性啊,所以越往深越是父级,你没看到最深的一层是 Object 吗?
|
3
rodrick 2021-10-04 21:28:37 +08:00 1
class 里的函数 就相当于是 Student.prototype.helloStudent = function()xx
class 只是语法糖 建议先看看 js 的原型链 理解一下关系 |
4
Biwood 2021-10-04 21:35:14 +08:00 via Android
原型和构造函数需要区分一下,你看的其实是原形链,这没有问题,ES6 引入的 class 本质上只是原型式继承的语法糖,当你理解了原型链的工作机制就会明白为什么是这样了
|
5
movq OP |
8
h503mc 2021-10-04 21:57:22 +08:00 1
这是我的理解
jack(PrimaryStudent 的实例)的原型是一个 Student 实例(因为 PrimaryStudent 类扩展了 Student 类) 所以 helloPrimaryStudent 方法在 jack.[[Prototype]]实例上并且 jack.[[Prototype]]的名字是 Student 同理,jack 原型的原型是一个 Object 实例(因为 Student 类默认扩展 Object 类) 所以 helloStudent 方法在 jack.[[Prototype]].[[Prototype]]实例上并且 jack.[[Prototype]].[[Prototype]]的名字是 Object 不要被 object 的构造函数的类名骗了 @seakingii #1 +1 |
10
vance123 2021-10-04 22:10:21 +08:00 2
当你没看完新手教程就开始玩游戏.jpg
详细解释请看<https://zh.javascript.info/class#shi-mo-shi-class> |
11
aristolochic 2021-10-04 22:14:22 +08:00 2
不要试图理解[[Prototype]]属性,这个不是学习 JavaScript 本身需要知道的,你看它写着是个原型,但很容易断章取义贻笑大方:
下面摘抄 [MDN 原文]( https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) : > __proto__的读取器(getter)暴露了一个对象的内部 [[Prototype]] 。对于使用对象字面量创建的对象,这个值是 Object.prototype (en-US)。对于使用数组字面量创建的对象,这个值是 Array.prototype 。对于 functions,这个值是 Function.prototype (en-US)。对于使用 new fun 创建的对象,其中 fun 是由 js 提供的内建构造器函数之一(Array, Boolean, Date, Number, Object, String 等等),这个值总是 fun.prototype 。对于用 js 定义的其他 js 构造器函数创建的对象,这个值就是该构造器函数的 prototype 属性。 > __proto__ 的设置器(setter)允许对象的 [[Prototype]] 被变更。前提是这个对象必须通过 Object.isExtensible() 判断为是可扩展的,如果不可扩展,则会抛出一个 TypeError 错误。要变更的值必须是一个 object 或 null,提供其它值将不起任何作用。 那么很明显,无论你的 Student 类还是 PrimaryStudent 类,很显然都不是 JavaScript 的内建构造器函数,new 出来的 [[Prototype]]只会是其对应的函数的 prototype 。PrimaryStudent 的 prototype 是 Student,Student 的 prototype 是 Object,显示十分正确。 |
12
seakingii 2021-10-04 22:19:15 +08:00
Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
|
13
Biwood 2021-10-04 22:30:51 +08:00 via iPhone 1
@movq
因为 js 一开始的设计里面就没有类的概念,只有 prototype,用 prototype chain 来实现继承,现在虽然有声明类的语法,但底层运行还是 prototype 的逻辑 在没有 class 的时代,只要你声明了一个函数(即类),它就默认带有 prototype,值为 {},当你 new 一个实例的时候,本质上就是产生一个 object,这个 object 默认的属性和方法都直接继承自函数的 prototype,而且这个 object 本身又可以作为另外一个函数的 prototype,这就形成了“链”,也就是继承 (感觉不是能轻易理解,必须回到没有 class 的时代手动实现一遍原型继承,然后结合具体的项目才行) |
14
rabbbit 2021-10-04 22:37:59 +08:00
|
15
seakingii 2021-10-04 22:44:50 +08:00
```
//第一步: 使用早期语法定义了一个类"Student",现在的 Class 本质上差不多,可以算是语法糖 function Student() { //类有个方法 this.helloFromStudent = function () { console.log("====== call helloFromStudent"); }; } //实例化类 const inst1 = new Student(); //调用方法 inst1.helloFromStudent(); //打印,这时会看到 "Student"有个下属方法叫"helloFromStudent",同时它的原型是"Prototype",和 Class 语法不同的是,这时打印看到"helloFromStudent"方法没有挂成原型上,实际调用效果是一样的 console.dir(inst1); //第二步:直接在原型上添加了一个新的方法"method2" //这也是脚本语言特性,可以动态更改类... Student.prototype.method2 = function () { this.helloFromStudent(); console.log("=====call method2"); }; //又构造了一个 const inst2 = new Student(); inst2.method2(); //这时可以看到,直接在 Student 上有个方法 helloFromStudent,在原型上有另一个方法"method2",实际上都能调用... console.dir(inst2); console.log(Student.prototype.helloFromStudent); //undefined , helloFromStudent 方法直接定义在 Student,没有定义在原型上 console.log(Student.prototype.method2); //和我的例子相比,现在的 class 语法把方法都放在 prototype 上了. //第三步:在原型上定义一个同名方法.... Student.prototype.helloFromStudent = function () { console.log("====== call helloFromStudent,我是放在原型上的方法"); }; //又构造了一个 const inst3 = new Student(); inst3.method2(); //打印结构,这时会看到有意思的情况:helloFromStudent 方法同时有两个,一个是 Student 的直接下级,一个是在原型上,实际调用时,原型上的不起作用,说明直接下级的优先级高 console.dir(inst3); //第四步:定义一个方法,删除直接下属的 helloFromMethod Student.prototype.deleteHelloFromStudent = function () { //删除直接方法"helloFromStudent" delete this["helloFromStudent"]; }; //又构造了一个 const inst4 = new Student(); inst4.deleteHelloFromStudent(); inst4.method2(); //打印结构,这时会看到有意思的情况:helloFromStudent 方法只有一个,是在原型上了...然后原型上的 helloFromStudent 又起作用了 console.dir(inst4); ``` |
16
h503mc 2021-10-04 23:02:03 +08:00
|
17
seakingii 2021-10-04 23:09:12 +08:00
|
18
xavierchow 2021-10-04 23:55:29 +08:00 2
这是个很好的问题,
我们定义 new 出来的子类实例 primaryStudent 为 p, 则 p.__proto__ === PrimaryStudent(class).prototype, PrimaryStudent.prototype.__proto__ = Student(class).prototype. 这个是我们关于原型链的基本理解,不会有问题,题主的困惑在于为什么在 web console 中,p.__proto__ 会显示成 Student? 我在 node.js/chrome/safari 中分别尝试了一下, 1. 在 node.js 和 safari 中 p.__proto__ 为 "PrimaryStudent" ``` Welcome to Node.js v12.18.2. Type ".help" for more information. > class Student { ... constructor(name) { ..... this.name = name; ..... } ... ... helloStudent() { ... console.log('student'); ... } ... } undefined > > class PrimaryStudent extends Student { ... constructor(name, grade) { ..... super(name); ..... this.grade = grade; ..... } ... ... helloPrimaryStudent() { ... console.log('primarystudent'); ... } ... } undefined > var p = new PrimaryStudent('john', 5) undefined > p.__proto__ PrimaryStudent {} > PrimaryStudent.prototype PrimaryStudent {} > ``` 2. 在 chrome 中, 如贴主所截图,p.__proto__为 "Student" ``` var p = new PrimaryStudent('john', 5) p.__proto__ Student {constructor: ƒ, helloPrimaryStudent: ƒ} constructor: class PrimaryStudent helloPrimaryStudent: ƒ helloPrimaryStudent() ``` 个人认为,其实在不同环境中,PrimaryStudent.prototype(即 p.__proto)还是同样的 object:{constructor: PrimaryStudent,prototype: Student.prototype}, 只不过在 node.js 和 safari 中,解释器用 constructor 来称呼这个 object, 在 chrome 中,它用 prototype 来称呼这个 object,不知道这个有没有回答到贴主的问题, 当然如有错误请各位指正。 |
19
Rocketer 2021-10-05 00:19:20 +08:00 via iPhone
楼主的根本问题在于角度不统一,一会儿 class 一会儿原型链,晕就对了。
很多人都说过了,class 是个语法糖,再说直白点就是——js 根本没有 class 。 你写的 class 会被“编译”(不准确,但可以这么理解)成原始的 js,也就是原型链。原型链是 js 这门语言的新知识点,想要彻底理解需要专门学一下,不能用 java 的思想来直接理解它。 不过个人认为原型链也是 js 的糟粕。既然 es6 开始推 class,那新学的人就坚守 class 吧。原型链不看不学不关心,反正实战中也用不到了。 |
20
NightCatS 2021-10-05 00:35:03 +08:00 2
@movq 楼上似乎都没 get 到楼主想要问的点:“为什么 helloPrimaryStudent 函数显示在 Student 类里?”
答:然而 helloPrimaryStudent 函数的确没有被挂载到 Student 原型上,楼主是被 Chrome 的输出误导了。 Chrome 输出中“[[Prototype]]: Student”的含义是:对象 jack 的原型是 Student,下面展开的属性并非是 Student 的原型属性,而是 jack.__proto__ 或者说 PrimaryStudent.protoype 。而 helloPrimaryStudent 函数自然是在 PrimaryStudent.protoype 上的。 |
21
seakingii 2021-10-05 00:53:14 +08:00 1
|
22
autoxbc 2021-10-05 13:57:42 +08:00
我单纯的认为 Chrome 显示错了,给每一层 [[Prototype]] 标记了错误的名字,Firefox 做的是正确的,可以对比看看
|
23
autoxbc 2021-10-05 14:13:44 +08:00
或者说 Chrome 的做法是反直觉的,PrimaryStudent 的原型是个 Student 实例这种拗口的显示属于画蛇添足,树形图本身就有完整的继承关系
|
25
zxCoder 2021-10-05 15:55:03 +08:00
回答 lz 的问题,(历史包袱
|
26
legendecas 2021-10-06 01:24:10 +08:00
显示 [[Prototype]] xxx 不正确是 chrome devtools 的 bug -。- https://bugs.chromium.org/p/chromium/issues/detail?id=1255695
|
27
wizardpisces 2021-10-11 12:21:03 +08:00
建议先读一下《 javascript 高级程序设计》继承部分,先理解不用 class 语法糖,只用 funtion 做继承,然后就明白了
|