采纳答案成功!
向帮助你的同学说点啥吧!感谢那些助人为乐的人
关于寄生组合方式问题:
这样写感觉也行, 我直接赋值原型,不使用middle,这样写问题出在哪里
答案:
function Parent (name, age) {
this.name = name
this.age = age
}
Parent.prototype.friends = ["xiaozhang", "xiaoli"]
Parent.prototype.eat = function () {
console.log(this.name + " 吃饭");
}
function Son (name, age, favor, sex) {
Parent.call(this, name, age)// TS继承中使用super
this.favor = favor // 兴趣爱好
this.sex = sex
}
Son.prototype = Parent.prototype// S101
// 给Son.prototype原型增加了方法,就等于给Parent.prototype增加了方法
Son.prototype.step = function () {
console.log(this.name, "爱好", this.favor);
}
let parent = new Parent("大山", 33);
parent.step();// 导致父对象也可以调用子类的step独有方法,这就不正确了,这就是为什么不能直接Son.prototype = Parent.prototype的原因
let sonobj = new Son("张三", 23, "打篮球", "男");
sonobj.step();
Son.prototype = Parent.prototype 这里后来我得到证实: Son.prototype 这样赋值后, Son.prototype 其实就是 Parent.prototype 所以才会访问所谓的子类方法,其实那个方法在赋值以后是父类原型的方法,您说对吗 """ Son.prototype {step: ƒ, constructor: ƒ} step: ƒ ()constructor: ƒ Parent()[[Prototype]]: Object """ 这里的原型构造函数指向的是Parent()函数本身, 通过这点来判断的
这个问题还是 你问的第一个问题的演变, 不难理解 function Parent (name, age) { this.name = name this.age = age } Parent.prototype.friends = ["xiaozhang", "xiaoli"] Parent.prototype.eat = function () { console.log(this.name + " 吃饭"); } function Son (favor, sex) { this.favor = favor // 兴趣爱好 this.sex = sex } let sonobj = new Son("打篮球", "男"); // S100..... Son.prototype = Parent.prototype// S101 // 1.因为一开始[ S100 行之前】 Son.prototype 对象属性和 sonObj 对象 // 都指向了一个 Son 的原型对象空间 // 2.当执行完 S101 行 [Son.prototype = Parent.prototype] 代码后 // Son.prototype 和 Parent.prototype 都 指向了 Parent 的原型对象空间 // 但 sonObj 对象 还指向着原来的 Son 的原型对象空间, // eat()方法在Parent 的原型对象空间,自然就无法访问 sonobj.eat();// 无法访问eat() 然后你再取思考Son.prototype = Parent.prototype 这样会带来什么问题
原因2:修改__proto__还可能带来性能上的损耗(本例不存在这个问题)
这个问题涉及到V8引擎方面的问题了,答案很长(给别的同学解答过,直接复制过来,你可以了解下即可 )
V8 引擎对于方法被调用(传入相同类型的参数)多次时 或 对象中的属性或方法多次被调用时,会使用 JIT 将函数编译成二进制代码【热编译】,以后再执行传入相同类型的参数的方法或对象中的属性或方法时,就直接执行,无需重复解释代码从而提高效率
本质上就是把代码转换成静态类型固定不变,并且生成一个类模板来记录属性或方法中各个变量的偏移量【就通过类模板来确定位置,关于类模板可以理解执行某个方法或对象属性中的属性或者方法时V8生成的一个记录某段执行代码中变量或方法的位置【偏移量】,以后在执行相同方法或访问对象中的相同方法或属性时可以直接从这个位置查找,从而提高效率。
但如果这时对象中 比如prototype原型对象空间的属性或方法被 __proto__属性给修改了,那么会导致V8引擎 所以的类模板记录下的偏移量要重新计算了,但很显然就把本来高效率执行的 固定不变的JIT编译好的静态类型重新退化成了动态类型【这就是所谓的字典模式】 并且需要再度多次执行,才会再次被 JIT 将函数编译成新的二进制代码,从而营销效率
说__proto__来修改属性影响性能,其实很容易误导大家, 因为运行时 任何数据的变化都会影响 JIT已经生成的变量的静态偏移量失效,比如方法参数类型的改变,对象属性中的值改变, 只是说__proto__它指向的是原型对象对象空间,而原型上的方法又是高频使用,所以如果我们多次使用原型空间上某个方法,比如show方法,JIT会将这个方法编译成二进制代码,固定成静态类型,并这时突然使用对象实例.__proto__修改了原型空间上的show方法的指向空间或删除了其中原型上的某个属性,刚才静态类型又退化成了动态类型。
但是我们发现 TS继承 JS继承 依然使用了 Son.__proto__=Parent 类似的写法 ,这样写也没有问题,而更多的要避免的是 对象实例变量.__proto__的使用,因为对象可以有很多,并且它修改的是高频使用的原型对象空间,也就是对象.__proto__会对静态化的”别人“ 造成影响,但 Son.__proto__=Parent 就不用担心,因为我们 Son.__proto__=Parent 目的是让Son来访问Parent类中的静态属性或静态方法,而且仅此用而已,自写自用。
1. 我们先来看 之前有个同学问过的问题: 看完后,你尝试理解和解答下你现在的问题,给你一个思考的空间,看看能否想到,其实答案很类似
问题是:老师我有一点不明白,既然middle是为了连接 ChinesePeople.prototype 与 People.prototype,People.call(this)已经访问了People构造函数里面的属性,那么直接使用 ChinesePeople.prototype.__ proto __ = People.prototype去访问People原型上的方法可以么?为什么还要new People呢?
我解答如下:【内容多,耐心看完,定能理解】
尽管两种方法都能达到目的,但使用__proto__来改变带来不好的问题!
1. 原因1:修改原型或对象的原型链属性__proto__(就是[[Prototype]])是一个不好的编码习惯
它改变了原型链的链级指向关系,破坏了原型链的稳定性,导致原型链上依赖的方法和属性都会受到影响
本例中的影响小一点,但依然存在影响, 影响如下:
我们并不希望S100行创建的son对象变量在S102处能调用 Parent.prototype 上的方法,因为它是我们 S101 行 改变指向之前发生的代码,但是实际能调用, 这就是修改__proto__代码带来的问题!
let son = new Son("爱好篮球", "男");// S100
Son.prototype.__proto__ = Parent.prototype// S101
Son.prototype.constructor = Son;// 让Son类的对象或函数原型.prototype指向的原型对象空间【new Parent()对象空间】有一个constructor属性
console.log("Son.prototype来看看:", Son.prototype);
console.log("abc..哦哦");
son.eat();// S102 调用 Parent原型上eat()方法成功
这个问题还是 你问的第一个问题的演变, 不难理解 function Parent (name, age) { this.name = name this.age = age } Parent.prototype.friends = ["xiaozhang", "xiaoli"] Parent.prototype.eat = function () { console.log(this.name + " 吃饭"); } function Son (favor, sex) { this.favor = favor // 兴趣爱好 this.sex = sex } let sonobj = new Son("打篮球", "男"); // S100..... Son.prototype = Parent.prototype// S101 // 1.因为一开始[ S100 行之前】 Son.prototype 对象属性和 sonObj 对象 // 都指向了一个 Son 的原型对象空间 // 2.当执行完 S101 行 [Son.prototype = Parent.prototype] 代码后 // Son.prototype 和 Parent.prototype 都 指向了 Parent 的原型对象空间 // 但 sonObj 对象 还指向着原来的 Son 的原型对象空间, // eat()方法在Parent 的原型对象空间,自然就无法访问 sonobj.eat();// 无法访问eat() 然后你再取思考Son.prototype = Parent.prototype 这样会带来什么问题
登录后可查看更多问答,登录/注册