call、apply、bind
- 怎么利用 call、apply 来求一个数组中最大或者最小值 ?
- 如何利用 call、apply 来做继承 ?
- apply、call、bind 的区别和主要应用场景 ?
- call 跟 apply 的用法几乎一样,唯一的不同就是传递的参数不同,call 只能一个参数一个参数的传入。
- apply 则只支持传入一个数组,哪怕是一个参数也要是数组形式。最终调用函数时候这个数组会拆成一个个参数分别传入。
- 至于 bind 方法,他是直接改变这个函数的 this 指向并且返回一个新的函数,之后再次调用这个函数的时候 this 都是指向 bind 绑定的第一个参数。
- bind 传参方式跟 call 方法一致。
适用场景:
求一个数组中最大或者最小值
// 如果一个数组我们已知里面全都是数字,想要知道最大的那个数,由于 Array 没有 max 方法,Math 对象上有
// 我们可以根据 apply 传递参数的特性将这个数组当成参数传入
// 最终 Math.max 函数调用的时候会将 apply 的数组里面的参数一个一个传入,恰好符合 Math.max 的参数传递方式
// 这样变相的实现了数组的 max 方法。min 方法也同理
const arr = [1,2,3,4,5,6]
const max = Math.max.apply(null, arr)
console.log(max) // 6
参数都会排在之后
// 如果你想将某个函数绑定新的`this`指向并且固定先传入几个变量可以在绑定的时候就传入,之后调用新函数传入的参数都会排在之后
const obj = {}
function test(...args) { console.log(args) }
const newFn = test.bind(obj, '静态参数1', '静态参数2')
newFn('动态参数3', '动态参数4')
利用 call 和 apply 做继承
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
// Animal.call(this) 的意思就是使用 this 对象代替 Animal 对象,那么
// Cat 中不就有 Animal 的所有属性和方法了吗,Cat 对象就能够直接调用 Animal 的方法以及属性了
var cat = new Cat("TONY");
cat.showName(); //TONY
将伪数组转化为数组(含有 length 属性的对象,dom 节点, 函数的参数 arguments)
// case1: dom节点:
<div class="div1">1</div>
<div class="div1">2</div>
<div class="div1">3</div>
let div = document.getElementsByTagName('div');
console.log(div); // HTMLCollection(3) [div.div1, div.div1, div.div1] 里面包含length属性
let arr2 = Array.prototype.slice.call(div);
console.log(arr2); // 数组 [div.div1, div.div1, div.div1]
//case2:fn 内的 arguments
function fn10() {
return Array.prototype.slice.call(arguments);
}
console.log(fn10(1,2,3,4,5)); // [1, 2, 3, 4, 5]
// case3: 含有 length 属性的对象
let obj4 = {
0: 1,
1: 'thomas',
2: 13,
length: 3 // 一定要有length属性
};
console.log(Array.prototype.slice.call(obj4)); // [1, "thomas", 13]
判断变量类型
let arr1 = [1,2,3];
let str1 = 'string';
let obj1 = { name: 'thomas' };
//
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
console.log(fn1(arr1)); // true
// 判断类型的方式,这个最常用语判断 array 和 object ,null( 因为 typeof null 等于 object )
console.log(Object.prototype.toString.call(arr1)); // [object Array]
console.log(Object.prototype.toString.call(str1)); // [object String]
console.log(Object.prototype.toString.call(obj1)); // [object Object]
console.log(Object.prototype.toString.call(null)); // [object Null]
总结:
- 当我们使用一个函数需要改变 this 指向的时候才会用到
call
apply
bind
- 如果你要传递的参数不多,则可以使用 fn.call(thisObj, arg1, arg2 ...)
- 如果你要传递的参数很多,则可以用数组将参数整理好调用 fn.apply(thisObj, [arg1, arg2 ...])
- 如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用 const newFn = fn.bind(thisObj); newFn(arg1, arg2...)
手写 bind
Function.prototype.myBind = function(context, ...args) {
// 设置 fn 为调用 myCall 的方法
const fn = this
args = args ? args : []
// 设置返回的一个新方法
const result = function(...newFnArgs) {
// 如果是通过 new 调用的,绑定 this 为实例对象
if (this instanceof result) {
fn.apply(this, [...args, ...newFnArgs]);
} else { // 否则普通函数形式绑定 context
fn.apply(context, [...args, ...newFnArgs]);
}
}
// 绑定原型链
result.prototype = Object.create(fn.prototype);
// 返回结果
return result;
};
this.a = 1;
const fn = function() {
this.a = 2;
console.log(this.a);
}
fn.myBind(fn);
fn();
实现 apply
Function.prototype.myApply = function (context, args) {
//这里默认不传就是给window,也可以用es6给参数设置默认参数
context = context || window
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol()
context[key] = this
//通过隐式绑定的方式调用函数
const result = context[key](...args)
//删除添加的属性
delete context[key]
//返回函数调用的返回值
return result
}
实现 call
//传递参数从一个数组变成逐个传参了,不用...扩展运算符的也可以用arguments代替
Function.prototype.myCall = function (context, ...args) {
//这里默认不传就是给window,也可以用es6给参数设置默认参数
context = context || window
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol()
context[key] = this
//通过隐式绑定的方式调用函数
const result = context[key](...args)
//删除添加的属性
delete context[key]
//返回函数调用的返回值
return result
}
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »