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
.......求解惑
我赞同一楼的回答,但是还有一问想请解答:
当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里单独创建一个属性,而是修改的是原型上值?
没看懂,你这描述里面没有箭头函数啊?
如果你问的就是你所写的代码,那可以这样解释:因为你没有执行实例上的这个函数啊!
tree2.addLeaf();
那当然最后tree2.leaf
还是0了!
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,你倒数第二句语句打错了
console.log(leaf) NaN
很简单,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)
来晚了,根据楼主的更新,谈下自己的理解,抛砖引玉。。。:
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。
首先是箭头函数内部 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 绑定到希望的原型对象上.