在看Promise迷你书遇到一段代码一开始没有理解,然后就查了一下bind的使用以及函数的值传递与引用传递。代码大致如下:
function recordValue(results, value) {
results.push(value);
return results;
}
var pushValue = recordValue.bind(null, []);
console.log(pushValue(1))
console.log(pushValue(2))
一开始我以为结果会是
[1]
[2]
但是实际结果是
[1]
[1,2]
function.prototype.bind
然后以为bind的参数我理解的有问题,我去查了一下api,下面是bind函数的解释。
bind()函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的
call
属性)。当新函数被调用时 this值绑定到bind()的第一个参数,该参数不能被重写。绑定函数被调用时,bind()也接受预设的参数提供给原函数。一个绑定函数也能使用new
操作符创建对象:这种行为就像把原函数当成构造器。提供的 this值被忽略,同时调用时的参数被提供给模拟函数。
简单来说就是改变某个方法内部的this指向,同时可以指定该函数的参数。我和理解的没什么大问题,我的理解就是它将recordValue
的this
绑定为null
,并且将第一个参数绑定为[],所以我会觉得结果是[1]
和[2]
。然后我打断点发现第二次执行pushValue
的时候recordValue
函数的第一个参数变成了[1],不再是[]
。所以我就考虑到了函数的引用传递。
然后我试了一下改造一下,结果和我第一次预期的一样
function recordValue(results, value) {
results = results+value;
return results;
}
var pushValue = recordValue.bind(null, "");
console.log(pushValue("hello"))
console.log(pushValue("word"))
这个时候结果又变成了
hello
world
这个时候我再改造一下
function recordValue(results, value) {
results.a = results.a + value
return results;
}
var pushValue = recordValue.bind(null, {a:0});
console.log(pushValue(1)) //
console.log(pushValue(2)) //
//-------结果----------//
{ a: 1 }
{ a: 3 }
这个时候就明显的是引用传递所以导致的第二次执行的时候第一个参数变了,不再为[]
。
最上面的代码var pushValue = recordValue.bind(null, []);
在绑定参数为[]
的时候,相当于初始化一个数组对象,而数组不是基本类型,是引用类型,作为函数的参数的时候就是引用传递。
js的变量类型
js的数据类型分为两种类型:
1.基本类型
包括Undefined, Null, Boolean, Number和String五种基本数据类型
2.引用类型
保存在内存中的对象们,不能直接操作,只能通过保存在变量中的地址引用对其进行操作
值传递
所以当函数的参数为基本类型的时候,它将保存的值’hello‘赋值给形参,他们只是值一样,地址不同。所以函数体内不管怎么改变形参,实参都不会跟着改变
引用传递
那么当参数为引用类型的时候,实参将其内部保存的对象的内存地址赋值给形参,他们两个保存的是一个内存地址,指向内存堆中的同一个对象,有点类似c中的指针。这个时候形参改变了其某个属性的值的时候其实改变的是指向的内存堆中对象的属性值。所以函数执行完成后,实参内存的地址虽然不变,但指向的对象已经改变了,所以实参会跟着形参变。
所以函数执行完毕后,实参从[]变成了[1]。
还有一种情况,如果形参在函数体内改变的不是引用的对象,而是重新赋值,比如指向一个新的对象results = {x:1}
或者赋值一个基本类型results = 0
,那么实参不变。因为指向一个新的变量都是改变形参内存中存的值,这个新的值可以是一个基本类型也可能是新的内存对象的地址。值变了之后,但实参内存中的值还是之前对象的地址,所以和形参不一样。
其实,引用传递也是值传递,只是传递的是引用罢了。之所以有引用传递这种说法应该是为了让大家更好明白概念。