yizihan / blog

Record
1 stars 0 forks source link

apply、call、bind #10

Open yizihan opened 6 years ago

yizihan commented 6 years ago

call、apply用法

call和apply都是为了改变某个函数运行时的 上下文(context) 而存在的,也就是改变函数体内部this的指向。

JS函数存在 「定义时上下文」「运行时上下文」「上下文是可以改变的」 这样的概念。

function Fruit() {}
Fruit.prototype = {
    color: 'red',
    say: function() {
        console.log('My color is ' + this.color);
    }
}
var apple = new Fruit();    // 创建实例对象
apple.say();                // My color is red
// 使用call、apply更换对象
var banana = {color: 'yellow'}
apple.say.call(banana);     // My color is yellow

当一个Object没有某个方法(例子中的banana没有say方法),但其他的对象有(例子中apple有say方法),我们可以借助call或apply用其他对象的方法来操作。

new 过程详解

var o = new Foo();
// 首先 => var o = new Object(); JS会开辟一块内存,创建一个实例对象;
// 然后 => o.__proto__ = Foo.prototype; 将实例对象的__proto__指向构造函数的Prototype,继承所有属性和方法;
// 然后 => Foo.call(o); 将构造函数中的this指向实例对象

call、apply区别

var func = function(arg1, arg2) {...}

func.call(this, arg1, arg2)
func.call(this, [arg1, arg2])

call:需要把参数按顺序传递进去 apply:需要把参数放在数组里面

call、apply应用

数组之间追加

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
Array.prototype.push.apply(arr1, arr2);
// 调用Array原型的push方法,把arr2中的值添加到arr1中

获取数组中的最大值、最小值

var numbers = [1, 2, 3, 4, 5];
var max = Math.max.apply(Math, numbers);

验证是否是数组

function isArray(arr) {
    return Object.prototype.toString.call(arr) == '[object Array]'
}

类数组使用数组方法

var domNodes = Array.prototype.slice.call(document.getElementsByTagName('*'))
// 通过Array.prototype.slice.call转换为真正的、数组的、带length属性的对象,

实现一个log方法代理console.log

function log(msg) {
    console.log(msg)
}
log(1);     // 1
log(1, 2);  // 1

当传入的参数不确定时,上面的代码就失效了。

function log() {
    console.log.apply(console, arguments);
    // arguments 传递给函数的参数的类数组对象
}
log(1, 2);  // 1 2

接下来需要给输出的内容添加一个前缀字符串

function log() {
    var args = Array.prototype.slice.apply(arguments);
    args.unshift('log:');
    console.log.apply(console, args)
}

bind应用

bind方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为this,传入bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

fun.bind(thisArg[, arg1[, arg2[, ...]]])
// thisArg:当绑定函数被调用时,该参数会作为原函数运行时的this指向。
// arg1, arg2:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

在常见的单体模式中,通常我们会使用_this/that/self等保存this,这样我们在改变了上下文之后继续引用到原this。

var foo = {
    bar: 1,
    eventBind: function () {
        var _this = this;
        // 进入到以下代码时,上下文this发生了改变
        $('.someClass').on('click', function() {
            console.log(_this.bar);     // 1
        })
    }
}

使用bind()优雅的解决上下文问题

var foo = {
    bar: 1,
    eventBind: function() {
        $('.someClass').on('click', function() {
            console.log(this.bar);      // 1 
        }).bind(this);  // 添加绑定函数,指定点击事件函数内部的this调用 
    }
}

call、apply、bind比较

当你希望改变上下文环境之后,并非立即执行,而是回调函数时,使用bind()方法;而call/apply则会立即执行函数。

var obj = { x: 81 };
var foo = { 
    getX: function() {
        return this.x;
    }
}

console.log(foo.getX.bind(obj)());      // 81
console.log(foo.getX.call(obj));        // 81
consoel.log(foo.getX.apply(obj));       // 81

参考: MDN Function.prototype.bind() 深入浅出 妙用Javascript中apply、call、bind