Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

函数的call和apply方法 #23

Open
andyChenAn opened this issue Jan 25, 2019 · 0 comments
Open

函数的call和apply方法 #23

andyChenAn opened this issue Jan 25, 2019 · 0 comments
Labels
javascript javascript

Comments

@andyChenAn
Copy link
Owner

call方法

call方法调用一个函数,其具有一个指定的this值和指定的参数列表。

fun.call(thisArg, arg1, arg2, ...)

thisArg参数,表示的是在fun函数运行时指定的this值。这里需要注意的是,如果是在非严格模式下,指定null或者undefined为this值时,this会自动执行全局对象(window),同时值为原始值的this会执行该原始值的包装对象。

// 非严格模式下
var a = 1;
function foo () {
    console.log(this.a);
}
foo.call(null);
// 打印结果为:1
// 严格模式下
'use strict';
var a = 1;
function foo () {
    console.log(this.a);
}
foo.call(null);
// 报错

如果在非严格模式下,函数调用call方法中没有传入第一个参数,那么会自动执行全局对象(window),如果在严格模式下,那么this值会是undefined。

// 非严格模式下
var a = 1;
function foo () {
    console.log(this.a);
}
foo.call();
// 严格模式下
'use strict';
var a = 1;
function foo () {
    // 这里的this值是undefined
    console.log(this.a);
}
foo.call();
// 报错

用法

使用call方法调用父构造函数

当我们需要继承父构造函数时,我们可以通过在子构造函数里调用call方法来执行父构造函数,从而实现继承父构造函数的属性。

function Person (name , age) {
    this.name = name;
    this.age = age;
}

function Andy (name , age) {
    Person.call(this , name , age);
}

function Jack (name , age) {
    Person.call(this , name , age);
}

var andy = new Andy('andy' , 22);
var jack = new Jack('jack' , 23);

使用call方法来绑定this值

var name = 'jack';

function say () {
    console.log(this.name);
}

var obj = {
    name : 'andy'
}
say.call(obj);

要注意,如果上面代码中不传入obj这个对象,那么在非严格模式下this值会执行全局对象(window),在严格模式下this值会是undefined。

模拟call方法

  • 1、绑定this。
  • 2、可以传入参数。
  • 3、当第一个参数不传入时,或者传入的是null或undefined,在非严格模式下会指向全局对象(window)。
  • 4、调用call方法可以有返回值。

因为所有的函数都有call方法,那么我们可以直接在函数的原型上添加该方法,首先我们要做的是调用call方法来绑定this值,当函数调用call方法时,会执行这个函数,并给这个函数指定一个this值。

Function.prototype.call2 = function (context) {
    context.fn = this;
    context.fn();
    // 当调用完之后,就可以删除对象上的这个属性
    delete context.fn;
};
Function.prototype.call2 = function (context) {
    context.fn = this;
    context.fn();
    delete context.fn;
};
var a = 1;
var obj = {
    a : 2
}
function foo () {
    console.log(this.a);
}
foo.call2(obj);
// 打印的结果为:2

但是当我们不传入第一个参数的时候或者传入的是null或者undefined,在非严格模式下this值会指向window

Function.prototype.call2 = function (context) {
    context = context || window;
    context.fn = this;
    context.fn();
    delete context.fn;
};

除了绑定this值,还可以传入其他参数到执行的函数里面,一开始我想到的是使用es6语法的rest参数方式,但是忽然觉得如果要模拟怎么能用es6语法呢?可以参考其他人的方式使用eval()来实现

Function.prototype.call2 = function (context) {
    context = context || window;
    var args = [];
    // 这里我们需要获取除第一个参数之外的其他参数
    for (var i = 1; i < arguments.length ; i++) {
        args.push('arguments[' + i + ']');
    };
    
    context.fn = this;
    eval('context.fn('+ args +')');
    delete context.fn;
};
Function.prototype.call2 = function (context) {
    context = context || window;
    var args = [];
    for (var i = 1; i < arguments.length ; i++) {
        args.push('arguments[' + i + ']');
    };
    
    context.fn = this;
    // args是一个数组,这里主要用到了字符串与数组相加的隐式转换,数组会调用toString()方法转为字符串,如果是使用数组的join()方法来将数组转为字符串,会报错。
    eval('context.fn('+ args +')');
    delete context.fn;
};
var a = 1;
var obj = {
    a : 2
}
function foo (name , age) {
    console.log(this.a);
    console.log(name);
    console.log(age);
}
foo.call2(obj , 'andy' , 22);

除了上面讲的之外,调用call方法还可以有返回值,而返回值就是执行函数的返回值

Function.prototype.call2 = function (context) {
    context = context || window;
    var args = [];
    for (var i = 1; i < arguments.length ; i++) {
        args.push('arguments[' + i + ']');
    };
    
    context.fn = this;
    // 将结果保存并返回即可
    var res = eval('context.fn('+ args +')');
    delete context.fn;
    return res;
};

apply和call方法的区别

其实apply和call方法的主要区别就是传入的参数不同,call方法传入的是一个参数列表,而apply方法传入的是一个保存参数列表的数组。

apply方法模拟

Function.prototype.apply2 = function (context , arr) {
    var context = context || window;
    var args = [];
    var res = '';
    context.fn = this;
    if (!arr) {
        res = context.fn();
    } else {
        for (var i = 0 ; i < arr.length ; i++) {
            args.push('arr['+i+']');
        };
        res = eval('context.fn('+ args +')');
    }
    delete context.fn;
    return res;
}

参考冴羽的文章,但是自己应该要按照前辈们的思路再去实现一遍,来巩固自己的基础知识。

@andyChenAn andyChenAn added the javascript javascript label Jan 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
javascript javascript
Projects
None yet
Development

No branches or pull requests

1 participant