JavaScript设计模式系列—基础篇(二)this、call、apply和bind方法的详解和区别

转载请注明预见才能遇见的博客:http://my.csdn.net/

原文地址:https://blog.csdn.net/pcaxb/article/details/87622552

JavaScript设计模式系列—基础篇(二)this、call、apply和bind方法的详解和区别

目录

1.this指向

1.当函数作为对象的方法被调用时,this 指向该对象

2.作为普通函数调用时,this 总是指向全局对象(window)

3. 构造器调用,构造器里的 this 就指向返回的这个对象

4. Function.prototype.call 或 Function.prototype.apply 调用

5.严格模式下的this指向

6.with 和 eval 的情况 以后扩展

2.document.getElementById探索

3.改变this指向

1.使用 ES6 的箭头函数

2.在函数内部使用 _this = this

3.使用 apply、call、bind

4. apply、call、bind的使用和区别

1.call的使用规则

2.apply的使用规则

3.bind的使用规则

4.apply 和 call 的区别

5.bind 和 call 的区别

4.bind、apply、call案例

5.自己实现一个bind


arguments和数组函数扩展

arguments 是一个对象,不是一个数组,是一个类数组对象。有下标,但是不能像数组一样排序或者添加一个元素。

// 打印封装
function L() {
    console.log.apply(this,arguments);
}
function test(){
    //1.arguments添加元素
    L(arguments);//1 2
    Array.prototype.push.call(arguments,3);
    L(arguments);//1 2 3
    //2.arguments转换成数组
    var args1 = [].slice.call(arguments);
    L(args1);//[1,2,3]
    //es6
    var args2 = Array.from(arguments);
    L(args2);//[1,2,3]
    // 3.删除arguments的第一个元素,返回第一个元素的值
    var value = [].shift.call(arguments);
    L(value)//1
    // 4.数组追加
    var ct = [].concat.call(args1,[2,2]);
    L(ct);
}

 

1.this指向

JavaScript this 总是指向一个对象,具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而不是函数在声明或创建时确认的。在 es5 中,this 永远指向最后调用它的那个对象。

1.当函数作为对象的方法被调用时,this 指向该对象

var person = {
    age:20,
    getAge:function(){
        L(this === person);//true
        L(this.age);//20
    }
}
person.getAge()

2.作为普通函数调用时,this 总是指向全局对象(window)

var age = 20;
var getAge = function(){
    L(this.age);//20
}
getAge()//等同于window.getAge()


//或者:
var age = 22;
var person = {
    age:20,
    getAge:function(){
        L(this === person);//false
        L(this.age);//22
    }
}
var getAge = person.getAge;
getAge();

//或者
var age = 22;
var person = {
    age:20,
    getAge:function(){
        L(this === person);//true
        L(this.age);//20
    }
}

person.getAge();//true 20
window.person.getAge();//true 20 者两个是一样的

难点:作为普通函数调用,记住匿名函数的this永远指向window

document.getElementById('div01').onclick = function(){
    L(this);//div01
    function callback(){
        L(this);//window 
    }
    callback();
};

//使用call来修正this
document.getElementById('div01').onclick = function(){
    L(this);//div01
    function callback(){
        L(this);//div01
    }
    callback.call(this);
};

this永远指向最后调用它的那个对象,那么我们就来找最后调用匿名函数的对象,因为匿名函数名字,所以我们是没有办法被其他对象调用匿名函数的。所以说 匿名函数的this永远指向window。

var name = "window中的变量";

function fn() {
    var name = 'Pcaxb';
    innerFunction();
    function innerFunction() {
        console.log(55,this.name);      
        console.log(55,this);      
    }
    innerFunction();
}

//this 永远指向最后调用它的那个对象
fn();// window中的变量

 

3. 构造器调用,构造器里的 this 就指向返回的这个对象

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

var p = new Person("cc");
L(p.name)//cc this指向p

构造函数显式的返回一个对象的情况:

function Person(name){
    this.name = name;
    this.age = 12;
    return {
        name:"aa"
    }
}

var p = new Person("cc");
L(p.name)//aa p指向Person返回的对象
L(p.age)//undefined

构造函数显式的返回一个字符串的情况:

function Person(name){
    this.name = name;
    this.age = 12;
    return ""
}

var p = new Person("cc");
L(p.name)//aa this指向p
L(p.age)//12

4. Function.prototype.call Function.prototype.apply 调用

var age = 20;
var getAge = function(){
    L(this.age);
}
getAge()//20
getAge.call({age:22});//22

动态地 改变传入函数的 this

 

5.严格模式下的this指向

function logThis1(){
    L(this);//window
}
logThis1()

function logThis2(){
    "use strict"
    L(this);//undefined
}
logThis2()

 

6.with eval 的情况 以后扩展

 

2.document.getElementById探索

var div01 = document.getElementById("div01");
L(div01);//div01

var getId = document.getElementById;
L(getId("div01"));//错误

//说明:本来getElementById函数里面的this指向document,但是getId调用的时候改变了this指向,
把getElementById函数里面的this指向改成了window

//或者
var getId = document.getElementById;
L(getId.call(document,"div01"));//div01

//或者
function getId(){
    return document.getElementById.apply(document,arguments);
}
L(getId('div01'));//div01

 

3.改变this指向

    1)使用 ES6 的箭头函数
    2)在函数内部使用 _this = this
    3)使用 apply、call、bind

1.使用 ES6 的箭头函数


    <SCript type="text/javascript">
        var name = "window中的变量";

        var obj = {
            name : "Pcaxb",

            func1: function () {
                console.log(this.name)     
            },

            func2: function () {
                setTimeout(  function () {
                    this.func1()
                },100);
            }

        };

        //obj.func2() // this.func1 is not a function

    </SCript>

在不使用箭头函数的情况下,是会报错的,因为最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数。

2.在函数内部使用 _this = this
 

    <!-- 改造一 在函数内部使用 _this = this -->
    <SCript type="text/javascript">
        
        var name = "window中的变量";

        var obj = {
            name : "Pcaxb",

            func1: function () {
                console.log(11,this.name)     
            },

            func2: function () {
                var _this = this;
                setTimeout(  function () {
                    _this.func1()
                },100);
            }

        };
        obj.func2();//"Pcaxb"
    </SCript>

    <!-- 改造二 使用箭头函数 -->
    <SCript type="text/javascript">
        
        var name = "window中的变量";

        var obj = {
            name : "Pcaxb",

            func1: function () {
                console.log(22,this.name)     
            },

            func2: function () {
                setTimeout(()=>{
                    this.func1()
                },100);
            }

        };
        obj.func2();//"Pcaxb"
    
    </SCript>


箭头函数的 this 始终指向函数定义时的 this,而非执行时。箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined

3.使用 apply、call、bind
 

    <!-- 改造三 使用 apply、call、bind中的bind -->
    <SCript type="text/javascript">
        var name = "window中的变量";
        var obj = {
            name : "Pcaxb",

            func1: function () {
                console.log(33,this.name)     
            },

            // func2: function () {
            //     setTimeout(  function () {
            //         this.func1()
            //     }.bind(this),100);
            // }
            func2: function () {
                setTimeout(  function () {
                    this.func1()
                }.bind(obj),100);
            }

        };
        obj.func2() // Pcaxb 
    </SCript>

    <!-- 改造四 使用 apply、call、bind中的apply -->
    <SCript type="text/javascript">
        var name = "window中的变量";
        var obj = {
            name : "Pcaxb44",

            func1: function () {
                console.log(44,this.name)     
            },

            // func2: function () {
            //     setTimeout(  function () {
            //         this.func1()
            //     }.apply(this),100);
            // }
            func2: function () {
                setTimeout(  function () {
                    this.func1()
                }.apply(obj),100);
            }

        };
        obj.func2() // Pcaxb 
    </SCript>

    <!-- 改造五 使用 apply、call、bind中的call -->
    <SCript type="text/javascript">
        var name = "window中的变量";
        var obj = {
            name : "Pcaxb55",

            func1: function () {
                console.log(55,this.name)     
            },

            func2: function () {
                setTimeout(  function () {
                    this.func1()
                }.call(this),100);
            }
            // func2: function () {
            //     setTimeout(  function () {
            //         this.func1()
            //     }.call(obj),100);
            // }

        };
        obj.func2() // Pcaxb 
    </SCript>

 

4. apply、call、bind的使用和区别

1.call的使用规则

call接受一系列参数,第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。

fun.call(thisArg, arg1, arg2, ...)
        var arr = [1, 2, 3, 89, 46]
        var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4])//89

        // 案例:
        var obj = {
            message: 'My name is: '
        }

        function getName(firstName, lastName) {
            console.log(this.message + firstName + ' ' + lastName);
        }

        getName.call(obj, 'Dot', 'Dolby');//My name is: Dot Dolby

2.apply的使用规则

apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组也可以是arguments对象或者类数组。当第一个参数为null、undefined的时候,默认指向window。

fun.apply(thisArg, [argsArray])
        var arr1 = [1,2,3,89,46]
        var max1 = Math.max.apply(null,arr)//89

        // 案例:
        var obj = {
            message: 'My name is: '
        }

        function getName(firstName, lastName) {
            console.log(this.message + firstName + ' ' + lastName)
        }

        getName.apply(obj, ['Dot', 'Dolby'])// My name is: Dot Dolby

3.bind的使用规则

bind接受一系列参数,和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。

function.bind(thisArg[, arg1[, arg2[, ...]]])
        var obj = {
            name: 'Dot'
        }

        function printName() {
            console.log(this.name)
        }

        var dot = printName.bind(obj)
        console.log(dot) // f
        dot()  // Dot

bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数。而原函数 printName 中的 this 并没有被改变,依旧指向全局对象 window。

4.apply 和 call 的区别

call()方法的作用和 apply() 方法一样,区别就是call()方法接受的是参数列表(一系列的单独变量),而apply()方法接受的是一个参数数组。

function test(a,b,c){
    console.log(a,b,c);
}
test.call(this,1,2,3);//1 2 3
test.apply(this,[1,2,3]);//1 2 3

当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的 区别,JavaScript 的参数在内部就是用一个数组来表示的。一般情况下都是使用apply,如果明确参数个数,想一目了然的知道形参和实参关系的情况下,可以使用call来传递参数。

apply 和 call 当第一个参数为null、undefined的时候,默认指向window。

利用call和apply做继承:构造函数的继承 https://blog.csdn.net/pcaxb/article/details/100668246

5.bind 和 call 的区别

bind和call很相似,区别在于bind方法返回值是函数,我们必须手动去调用,以及bind接收的参数列表的使用。

参数使用的区别

function fn(a, b, c) {

console.log(a, b, c);

}

var fn1 = fn.bind(null, 'Dot',"Pig");



fn('A', 'B', 'C'); // A B C

fn1('A', 'B', 'C'); // Dot Pig A

fn1('B', 'C'); // Dot Pig B

fn1('B'); // Dot Pig B

fn.call(null, 'Dot'); // Dot undefined undefined

call 是把第二个及以后的参数作为 fn 方法的实参传进去,而 fn1 方法的实参实则是在 bind 中参数的基础上再往后排。

 

4.bind、apply、call案例

bind的使用

        //案例二
        function f1(a,b,c){
            console.log(a,b,c);
        }
        f1.bind(null)(1,2,4);//1 2 4

 求数组中的最大和最小值

        //案例三
        var arr = [1,2,3,89,46]
        var max = Math.max.apply(null,arr)//89
        var min = Math.min.apply(null,arr)//1

将类数组转化为数组

var trueArr = Array.prototype.slice.call(arrayLike)

使用 log 代理 console.log

function log(){
  console.log.apply(console, arguments);
}

 

5.自己实现一个bind

Function.prototype.selfBind = function(context){
    var _this = this;
    return function(){
        //context是传入的this对象
        _this.apply(context,arguments);
    }
}

var func1 = function(){
    L(this);
}.bind(this);
func1();

var func2 = function(){
    L(this);
}.selfBind(this);
func2();

匿名函数调用selfBind,所以selfBind里面的this是指向匿名函数的,this.apply调用的就是匿名函数,给匿名函数传context指向和参数。由于模拟bind,所以selfBind需要返回一个函数。如果匿名函数中直接使用this,这个this指向的是window,所以使用一个中间变量保存this。

bind的特性是实参实则是在 bind 中参数的基础上再往后排,代码优化

Function.prototype.selfBind = function(){
    var _this = this,
    //shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
    context = [].shift.call(arguments);//需要绑定的 this 上下文
    //slice() 方法可从已有的数组中返回选定的元素
    args = [].slice.call(arguments);// 剩余的参数转成数组
    console.log(context,args);
    return function(){
        //context是传入的this对象
        _this.apply(context,[].concat.call(args,[].slice.call(arguments)));
        // 执行新的函数的时候,会把之前传入的 context 当作新函数体内的 this 
        // 并且组合两次分别传入的参数,作为新函数的参数
    }
}

var func2 = function(a,b,c,d){
    L(this.name);//cc
    L(a,b,c,d);//22 33 1 2
}.selfBind({name:"cc"},22,33);
func2(1,2);

 

参考资料:call、apply和bind方法的用法以及区别  apply、call、bind 区别  MDN说明

 

JavaScript设计模式系列—基础篇(二)this、call、apply和bind方法的详解和区别

博客地址:https://blog.csdn.net/pcaxb/article/details/87622552

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值