【js】谈谈对this的理解

123 阅读3分钟

this是啥

  • 函数在被调用时会创建执行上下文,this是一个内部对象,只能在函数内部使用,总指向调用它的对象。
  • 谁把它执行的「在哪创建&在哪执行都没有必然的关系」this指向谁。

this的绑定规则

  • 函数执行,看方法前面是否有“点”,没有“点”,this是window「严格模式下是undefined」,有“点”,“点”前面是谁this就是谁
const fn = function fn() {
    console.log(this);
};
let obj = {
    name: 'OBJ',
    fn: fn
};
fn(); // 默认绑定 window
obj.fn();  // 隐式绑定 obj
  • 显式绑定->基于Function.prototype上的call/apply/bind修改this指向。 fn.call(obj) // obj
  • new绑定,构造函数体中的this是当前类的实例。
function People() {
    this.name = 'Tom';
    this.age = 2;
    console.log(this); //  People {name: 'Tom', age: 2}
}
let f = new People; 

  • 箭头函数没有this,箭头函数中的this指向最近的外层作用域中的this所指对象,一层层往外找,直到有this的定义。
 let demo = {
    name: 'DEMO',
    fn() {
        console.log(this);//demo
        setTimeout(function () {
            console.log(this); //window
        }, 1000);

        setTimeout(() => {
            console.log(this); //demo
        }, 1000);
    }
};
demo.fn(); 
  • 给当前元素的某个事件行为绑定方法,当事件行为触发,方法中的this是当前元素本身「排除attachEvent」

优先级

new绑定优先级 > 显式绑定优先级 > 隐式绑定优先级 > 默认绑定优先级,箭头函数

改变this的指向

call

原理:利用点机制,给CONTEXT设置一个属性,属性值一定是我们要执行的函数。

func函数基于__proto__找到Function.prototype.call,把call方法执行

在call方法内部「call执行的时候」 call(context->obj,...params->[10,20])

  • 把func中的this改为obj
  • 并且把params接收的值当做实参传递给func函数
  • 并且让func函数立即执行 func.call(obj, 10, 20); func.apply(obj, [10, 20]);
Function.prototype.mycall= function call(context,...params){
    // 原理 obj.xxx = func ;obj.xxx(...params)
    // 判断context是否为空
    context = context || window
    let func = this //this 为当前函数
    // 声明唯一属性,避免与context的属性冲突
    let key =Symbol('属性值')
    context[key] = func
    // 调用函数返回结果
    const res = context[key](...params)
    delete context[key]
    return res
}
//  调试 
const fn = function fn(b) {
    let name = 'QQ'
    console.log(this);
    return this.name + b
};
let obj = {
    name: 'OBJ',
    fn: fn
};
console.log(fn.mycall(obj,"hello"))
//输出
//{name: 'OBJ', fn: ƒ, Symbol(属性值): ƒ}
//OBJhello

注意:对象的属性名在不同情况下如何取值

  • 如果属性名称是常量(固定值),获取对应对象的属性值的方式有:

对象.属性名称
对象[属性名称]

二、如果属性名称是一个变量(不固定值),获取属性值方式只能是:

对象[属性名称] 传进去的属性名称是一个变量

apply

与call区别:传递的参数不同,apply传递的是一个数组

数组合并

let a = [1,2,3] 
let b = [4,5,6]
a.push.apply(a,b)
//a->(6) [1, 2, 3, 4, 5, 6]
//b->(3) [4, 5, 6]

bind

区别: func函数基于__proto__找到Function.prototype.bind,把bind方法执行

在bind方法内部

  • 和call/apply的区别:并没有把func立即执行
  • 把传递进来的obj/10/20等(上下文,参数)信息存储起来「闭包」
  • 执行bind返回一个新的函数 例如:proxy,把proxy函数绑定给元素的事件,当事件触发执行的是返回的proxy函数,在proxy内部,再去把func执行,把this和值都改变为之前存储的
Function.prototype.Mybind = function bind(context,...params){
    context = context || window
    let func = this
    return function(...args){
        // 执行方法并改变this
        return func.apply(context, [...params,...args,]);
    }
}
let value = "window"
function fun(bindParams,funParam){
    return this.value + bindParams+funParam
}
let obj = {
    value: "点击"
}
// 这两参数都是传给fun
let bindFun = fun.Mybind(obj,"bind参数")
console.log(bindFun('fun参数'))
//点击bind参数fun参数

类数组

无法使用数组方法

1.  函数里面的参数对象arguments;
2.  用getElementsByTagName/ClassName/Name获得的HTMLCollection;
3.  用querySelector获得的 NodeList。
转化为数组
#### array.form()
#### 扩展运算符...
#### 利用Array.prototype.slice
Array.prototype.slice.call(arguments)/[].slice.call(arguments)