javascript - 问一个JS继承的问题
高洛峰
高洛峰 2017-04-11 12:57:01
[JavaScript讨论组]
function Tree () {}

Tree.prototype.leaf = 0
Tree.prototype.addLeaf = function () {this.leaf++}

let tree1 = new Tree()
let tree2 = new Tree()

tree1.addLeaf()
tree1.addLeaf()

console.log(tree2.leaf) 

更新:这里我把箭头函数去掉下
为什么输出是0.......求解惑

高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

全部回复(7)
怪我咯

首先是箭头函数内部 this 指向的问题,
箭头函数内部本身没有 this ,内部 this 只能通过作用域链往上查找.
所以

Tree.prototype.addLeaf = () => {this.leaf++}

这里的 this 指向 window.

然后...没时间了, 领导要我打扫办公室卫生. 下面这个 demo 你先看着. 等会回来再说..
https://jsfiddle.net/pqjag5vj/

OK, 回来了, 题目也改了

Tree.prototype.addLeaf = function () {
    this.leaf++
}
tree1.addLeaf()
tree1.addLeaf()

说说执行 tree1.addLeaf() 这句代码的时候发生了什么,
JS 函数内部 this 指向只有在调用的时候才确定,
那么, tree1 调用 addLeaf 的时候, addLeaf 内部 this 是指向 tree1 的.
也就是说,执行 tree1.addLeaf() , addLeaf 函数其实相当于

function () {
    tree1.leaf++
}

然后就是实例属性与原型属性同名问题.
虽然可以实例可以通过原型链访问到原型中的值, 但是不能通过对象实例重写原型中的值.
所以, tree1.leaf 中的 leaf 就是在实例上创建的属性, 后面执行 tree1.addLeaf 的时候修改的也是 tree1 上的 leaf 属性, 并非原型上的 leaf 属性.

当 tree2 访问 leaf 属性的时候, 还是按照原型链往上找,
原型上的 leaf 并没有给修改, 所以还是 0.

那么, 我们怎么修改得让它符合预期, 使他修改原型上的属性呢, 一种是我上面给出的 demo , 通过 proto 对象实例的属性(它指向对象构造函数的原型对象) 来访问并修改原型上的属性.
还有通过apply, bind, call 等方法, 把 addLeaf 的 this 绑定到希望的原型对象上.

天蓬老师

来晚了,根据楼主的更新,谈下自己的理解,抛砖引玉。。。:

this.leaf++ 可以看做:

this.leaf = this.leaf + 1

this本身原来是是没有leaf属性的,所以会去this的原型链上找,结果在Tree.prototype找到了(Tree.prototype.leaf = 0),所以这句可以看成:

this.leaf = 0 + 1

相当于在this上增加了leaf属性为1,下次访问this.leaf已经不会从原型链取值了。
所以tree1执行完2次addLeaf之后是:

{ leaf: 2 }

而tree2还是空的,自身没有leaf属性,tree2上访问leaf属性仍会去原型链上找,原型上leaf还是0。

PHP中文网

很简单,this指向与原型问题。

    function Tree () {}
    
    Tree.prototype.leaf = 0 //这一步,所有的new Tree都会获得一个公共的 leaf 属性
    
    //关键在与这里的 this.leaf. 箭头函数没有独自的this,这里的this会指向的是window. 所以是 window.leaf++
    Tree.prototype.addLeaf = () => {this.leaf++}
    
    let tree1 = new Tree()
    let tree2 = new Tree()
    
    tree1.addLeaf() //window.leaf自加了1
    tree1.addLeaf() //window.leaf自加了1
    
    console.log(tree2.leaf) 
ringa_lee
console.log(leaf)
NaN
巴扎黑
function Tree () {}

Tree.prototype.leaf = 0
Tree.prototype.addLeaf = function () {this.leaf++;debugger}

let tree1 = new Tree()
let tree2 = new Tree()

tree1.addLeaf()
tree2.addLeaf()

console.log(tree2.leaf) 

结果为1,你倒数第二句语句打错了

大家讲道理

没看懂,你这描述里面没有箭头函数啊?
如果你问的就是你所写的代码,那可以这样解释:因为你没有执行实例上的这个函数啊!

  tree2.addLeaf();  

那当然最后tree2.leaf还是0了!

伊谢尔伦

我赞同一楼的回答,但是还有一问想请解答:
当Tree的原型对象里面有一个对象字面量属性时,情况似乎不太一样了:

var Tree = function(){};
Tree.prototype.men = {
    age:15
};
Tree.prototype.setAge = function(){this.men.age = 20};
var tree1 = new Tree();
var tree2 = new Tree();
tree1.setAge(20);
console.log(tree2.men); // {age:20}

请问,这里为什么又是20?为什么没有在tree1里单独创建一个属性,而是修改的是原型上值?

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号