1

call 方法:
object.method.call(targetObj[, argv1, argv2, .....])

apply 方法:
object.method.apply(targetObj[, [argv1, argv2 .....]])

call 和 apply 的作用没有太大区别,仅是在调用时传递参数的方式不同,call 是按位置参数传递,apply 是传递一个参数数组。

call 和 apply 方法的作用如下:

以 targetObject 替换 object 引入当前的执行上下文中。

也就是说当使用 object.call(targetObj) 后,当前执行上下文中的 object 对象已被替换为 targetObj,后续执行将以 targetObj 所持有的状态属性继续执行。

借用 object 的方法:
targetObj 替换 object 的实例去调用相应的方法。

// Person 引用类型
function Person(name, age) {
    this.name = name;
    this.age  = age;
    // Person 的对象方法
    this.getName = function () {
        console.log(this.name);
    }
}

// Person 的原型方法
Person.prototype.getInfo = function (joinStr, endLine) {
    joinStr = joinStr || "-";
    endLine = endLine || "!";
    console.log(this.name + joinStr + this.age + endLine);
}

// Boy 引用类型
function Boy(name, age) {
    this.name = name;
    this.age  = age;
}

var lilei  = new Boy("lilei", 18);

// 调用 Person 的原型方法
// 注:原型方法和对象方法并不对等,虽然二者皆可被对象继承 但原型链上只有原型方法 没有对象方法
// 所以对象方法还是要以下面实例化一个对象的方式去调用,不能直接通过原型链访问
Person.prototype.getInfo.call(lilei, "-", "!");
Person.prototype.getInfo.apply(lilei, ["-", "!"]);

// 调用 Person 的对象方法
var person = new Person("none", 0);
person.getName.call(lilei);
person.getInfo.apply(lilei, ["-", "!"]);

Boy 虽然并没有定义 getName 对象方法,也没有 getInfo 原型方法,但我们可以方便的使用 call/apply 将 Person 的方法应用到 Boy 的实例上。call/apply 的语意其实是说 Person “喊” Boy 的实例过来使用自己的方法。

实现引用类型的继承:

Javascript 其实没有类这一概念,我们平时使用的 Array, Date, Math 等严格来说被称作 “引用类型”。我们可以方便的使用 call/apply 来实现引用类型的对象方法的继承,从而让代码更加的精简。

继承分为 对象继承 和 原型链继承 两部分

// Person 引用类型
function Person(name, age) {
    this.name = name;
    this.age  = age;
    // Person 的对象方法
    this.getName = function () {
        console.log(this.name);
    }
}

// Person 的原型方法
Person.prototype.getInfo = function (joinStr, endLine) {
    joinStr = joinStr || "-";
    endLine = endLine || "!";
    console.log(this.name + joinStr + this.age + endLine);
}

// ======================================================
// Boy 引用类型
function Boy(name, age) {
    // 对象冒充继承
    // 将 Person 的对象成员继承过来
    Person.call(this, name, age);
}

// 原型链继承
// 将 Person 的原型属性继承过来
Boy.prototype = new Person();
// ======================================================

var lilei  = new Boy("lilei", 18);

lilei.getName();
lilei.getInfo();
console.log(Boy.prototype.getInfo);

注意这里 call/apply 只是继承父原型的对象方法,原型方法还需要单独的继承一次。

平时开发中比较经典的用例就是:

Array.prototype.slice.call(document.getElementsByTagName("p"));

如上代码会返回一个DOM元素数组,document.getElementsByTagName("p") 获取到的并不是真正意义上的数组,没办法使用 pop 或 push 等方法,Array.prototype.slice 的实现大致如下:

Array.prototype.slice = function (start, end) {
    var temp  = [];
    var start = start || 0;
    var end   = end || this.length - 1;
    
    for (var i = start; i <= end; i ++) {
        temp.push(this[i]);
    }
    
    return temp;
}

big_cat
1.7k 声望130 粉丝

规范至上