call apply bind
- apply 与 call 是为了改变this指向而出现的,如下:
function fruits () {}
fruits.prototype = {
color: 'red',
say: function () {
console.log('my color is ' + this.color)
}
}
var apple = new fruits;
apple.say(); // my color is red
var banana = {
color: 'yellow';
}
apple.say.call(banana); // my color is yellow
apple.say.apply(banana); // my color is yellow
- apply call 区别 作用完全一样,只是接受参数不太一样,call要列举参数,apply要传一个数组,如果参数个数不确定,用apply更好
var func = function (arg1, arg2) {
}
// 调用
func.call(this, arg1, arg2); // 第一个参数为指向的任何对象
func.apply(this, [arg1, arg2]); // 其他参数用数组形式传入
- 实例 数组追加
var arr1 = [12, 'foo', {name: 'mac'}];
var arr2 = ['doe', 34, 23];
Array.prototype.push.apply(arr1, arr2);
// arr1的值将变为[12, 'foo', {name: 'mac'}, 'doe', 34, 23]
获取数组中的最大值最小值
var numbers = [3, 0, 34, -43];
var maxInNumbers = Math.max.apply(Math, numbers),
maxInNumbers = Math.max.call(Math, 3,0, 34, -43);
验证是否是数组(toString()没有被重写的前提下)
function isArray (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
console.log的重写,可传入多个输出对象
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
// 加入前缀方法
function log(){
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
};
bind
bind() 与 apply 和 call 很相似,同样可以改变this指向 bind会创建一个新函数(绑定函数),创建时会传入bind方法的第一个参数作为this,第二个及以后的参数作为原函数参数来调用函数。
var docWrite = document.write;
// docWrite("hello");// Uncaught TypeError: Illegal invocation非法调用
docWrite.bind(document)("hello");
docWrite.call(document, "hello");
- 一个简单的绑定函数,使他无论怎么调用都有相同的this值
this.num = 9;
var mymodule = {
num: 81,
getNum: function() {
console.log(this.num);
}
};
mymodule.getNum(); // 81
var getNum = mymodule.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象
var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81
- 用bind() 来替代我们常用_this, self等保存this
var foo = {
num: 1,
eventBind: function () {
var self = this;
$('.class').on('click', function (event) {
console.log(self.num);
})
}
}
替换
var foo = {
num: 1,
eventBind: function () {
$('.class').on('click', function (event) {
console.log(this.num);
}.bind(this) ); // 传入的this就是foo
}
}
- 多次bind 在Javascript中,多次 bind() 是无效的。更深层次的原因是, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。
- bind的实现 上面的几个小节可以看出bind()有很多的使用场景,但是bind()函数是在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。这就需要我们自己实现bind()函数了。首先我们可以通过给目标函数指定作用域来简单实现bind()方法:
Function.prototype.bind = function(context){
self = this; //保存this,即调用bind方法的目标函数
return function(){
return self.apply(context,arguments);
};
};
考虑到函数柯里化的情况,我们可以构建一个更加健壮的bind():
Function.prototype.bind = function(context){
var args = Array.prototype.slice.call(arguments, 1),
self = this;
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply(context,finalArgs);
};
};
这次的bind()方法可以绑定对象,也支持在绑定的时候传参。继续,Javascript的函数还可以作为构造函数,那么绑定后的函数用这种方式调用时,情况就比较微妙了,需要涉及到原型链的传递:
Function.prototype.bind = function(context){
var args = Array.prototype.slice(arguments, 1),
F = function(){},
self = this,
bound = function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply((this instanceof F ? this : context), finalArgs);
};
F.prototype = self.prototype;
bound.prototype = new F();
return bound;
};
这是《JavaScript Web Application》一书中对bind()的实现:通过设置一个中转构造函数F,使绑定后的函数与调用bind()的函数处于同一原型链上,用new操作符调用绑定后的函数,返回的对象也能正常使用instanceof,因此这是最严谨的bind()实现。对于为了在浏览器中能支持bind()函数,只需要对上述函数稍微修改即可:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
return fToBind.apply(this instanceof fBound
? this
: oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的 aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 维护原型关系
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
// 下行的代码使fBound.prototype是fNOP的实例,因此
// 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
fBound.prototype = new fNOP();
return fBound;
};
}
apply、call、bind 三者比较
var obj = {
x: 22,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //如果没有(),将返回一个函数function(){return this.x}
//22console.log(foo.getX.call(obj)); //22console.log(foo.getX.apply(obj)); //22
三个输出的都是22,但是注意看使用 bind() 方法的,他后面多了对括号。也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。再总结一下:apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;apply 、 call 、bind 三者都可以利用后续参数传参;bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
仅作为个人学习、阅读笔记,如有不足之处请指教,如有侵权请联系本人即删除