资讯专栏INFORMATION COLUMN

js函数this理解?手写apply、call、bind就够了

Cciradih / 568人阅读

摘要:一是什么函数的内部属性,引用的是函数据以执行的环境对象。函数做为节点事件调用时指向节点本身做为构造函数实力化方法时指向实例对象箭头函数里的普通函数,由于闭包函数是执行的,所以指向箭头函数的指向函数创建时的作用域。

一、this是什么?

函数的内部属性,this引用的是函数据以执行的环境对象。也就是说函数的this会指向调用函数的执行环境

function a(){
    return  this
}
console.log( a() === window) //true

函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。

function a(){
    "use strict"
    return  this
}
console.log( a() === undefined) //true
二、this指向哪里

this做为函数的关键字,指向函数的调用对象。

大致的指向总结归纳为可以分为以下几种:

1.函数做为全局环境调用时
var name = "window";
function a(){
    return  this.name
}
a() ==="window" //true
2.函数做为对象方法调用时
var obj = {
    name:"obj",
    sayName:function(){
        return this.name
    }
}
console.log( obj.sayName() === "obj") //true

//稍微改动一下;添加下面代码。

var sayName = obj.sayName;
console.log( sayName() === "obj") //false

//此时,sayName函数里的this指向了window。

这里需要明白一点,函数名仅仅是一个包含指针的变量,函数是复杂数据类型,所以函数名就只是一个指针,指向堆中的内存地址!所以sayName此时只是复制了指针地址,所以,上面代码改写成下面就很清晰了。

var sayName = function(){
    return this.name
}
var obj = {
    name:"obj",
    sayName:sayName
}
console.log( obj.sayName() === "obj") //true
console.log( sayName() === "obj") //false
3.函数做为dom节点事件调用时
var container3 = document.getElementById("container3")
container3.onclick = function(){ //指向节点本身
    console.log(this) //
container3
}
4.做为构造函数实力化方法时
function A(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name)//指向实例对象
    }
}
var a = new A("aa");
a.sayName(); //aa
5.箭头函数里的this
var name = "window"
var obj = {
    name:"obj",
    fn:function(){    
       (function (){
        console.log(this.name)
       })()
    }    

}
obj.fn() //window

普通函数,由于闭包函数是window执行的,所以this指向window;
箭头函数的this指向函数创建时的作用域。

var name = "window"
var obj = {
    name:"obj",
    fn:function(){    
       (()=>{ //改成箭头函数
        console.log(this.name)
       })()
    }    

}
obj.fn()

改成箭头函数,后可以看出创建时的作用域是obj.fn函数执行是的作用域,也就是obj
三、this指向怎么改

js提供了一些可以改变函数执行作用域的方法。因为普通函数如果通过上面的写法来改变this执行时上下文,写法就太过于麻烦。

apply、call、bind用法

apply:
fn.apply(thisObj,数组参数)
定义:应用某一个对象的一个方法,用另一个对象替换当前对象
说明:如果参数不是数组类型的,则会报一个TypeError错误。

call:
fn.call(thisObj, arg1, arg2, argN)
apply与call的唯一区别就是接收参数的格式不同。

bind:
fn.bind(thisObj, arg1, arg2, argN)
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

apply、call、bind实现
apply的实现:

Function.prototype.myApply= function(context){
    context.fn = this;//1.将函数挂载到传入的对象
    var arg = [...arguments].splice(1)[0];//2.取参数
    if(!Array.isArray(arg)) {
        throw new Error("apply的第二个参数必须是数组") //3.限制参数类型为数组
    }    
    context.fn(arg) //4.执行对象的方法
    delete context.fn; //5.移除对象的方法
}

var obj = {
    name:"obj"
}
function sayName(arr){
    console.log(this.name,arr)
}
sayName.myApply(obj,[1,2,3]) //obj [1, 2, 3]
call实现:与apply的唯一区别就是参数格式不同

Function.prototype.myCall= function(context){
    context.fn = this;//1.将函数挂载到传入的对象
    var arg = [...arguments].splice(1);//2.取参数
    context.fn(...arg) //3.执行对象的方法
    delete context.fn; //4.移除对象的方法
}
var obj = {
    name:"obj1"
}
function sayName(){
    console.log(this.name,...arguments)
}
sayName.myCall(obj,1,2,3,5) //obj1 1,2,3,5


bind在mdn上的实现:

Function.prototype.myBind = function(oThis){
    if(typeof this !== "function"){
        throw new TypeError("被绑定的对象需要是函数")
    }
    var self = this
    var args = [].slice.call(arguments, 1)
    fBound = function(){ //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
        return self.apply(this instanceof fBound ? this : oThis, args.concat([].slice.call(arguments)))
    }
    var func = function(){}
    //维护原型关系
    if(this.prototype){
        func.prototype = this.prototype
    }
    //使fBound.prototype是func的实例,返回的fBound若作为new的构造函数,新对象的__proto__就是func的实例
    fBound.prototype = new func()
    return fBound
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106553.html

相关文章

  • 「中高级前端面试」JavaScript手写代码无敌秘籍

    摘要:第一种直接调用避免在不必要的情况下使用,是一个危险的函数,他执行的代码拥有着执行者的权利。来自于此外,实现需要考虑实例化后对原型链的影响。函数柯里化的主要作用和特点就是参数复用提前返回和延迟执行。手写路径导航 实现一个new操作符 实现一个JSON.stringify 实现一个JSON.parse 实现一个call或 apply 实现一个Function.bind 实现一个继承 实现一个J...

    Zhuxy 评论0 收藏0
  • 前端基础汇总

    摘要:及相关问题数据类型函数中指向原型作用域闭包面向对象对象创建模式继承严格模式与对象转换的方法添加属性,根据原型创建区别新特性解构赋值简化对象写法剪头函数三点运算符模板字符串形参默认值异步过程深拷贝与浅拷贝赋值与浅拷贝的区别浅拷贝的几种方法实现 js及es相关问题 数据类型函数中this指向——————原型作用域闭包——————面向对象对象创建模式继承——————Es5严格模式Json与j...

    2json 评论0 收藏0
  • 前端基础汇总

    摘要:及相关问题数据类型函数中指向原型作用域闭包面向对象对象创建模式继承严格模式与对象转换的方法添加属性,根据原型创建区别新特性解构赋值简化对象写法剪头函数三点运算符模板字符串形参默认值异步过程深拷贝与浅拷贝赋值与浅拷贝的区别浅拷贝的几种方法实现 js及es相关问题 数据类型函数中this指向——————原型作用域闭包——————面向对象对象创建模式继承——————Es5严格模式Json与j...

    laznrbfe 评论0 收藏0
  • javascript 基础之 call, apply, bind

    摘要:系统,扎实的语言基础是一个优秀的前端工程师必须具备的。第一个参数为调用函数时的指向,随后的参数则作为函数的参数并调用,也就是。和的区别只有一个,就是它只有两个参数,而且第二个参数为调用函数时的参数构成的数组。 系统,扎实的 javascript 语言基础是一个优秀的前端工程师必须具备的。在看了一些关于 call,apply,bind 的文章后,我还是打算写下这篇总结,原因其实有好几个。...

    xeblog 评论0 收藏0
  • js 面试官想了解你有多理解call,apply,bind?

    摘要:返回值这段在下方应用中有详细的示例解析。回调函数丢失的解决方案绑定回调函数的指向这是典型的应用场景绑定指向,用做回调函数。 showImg(https://segmentfault.com/img/remote/1460000019971331?w=1024&h=680); 函数原型链中的 apply,call 和 bind 方法是 JavaScript 中相当重要的概念,与 this...

    wuaiqiu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<