zhaobinglong / myBlog

https://zhaobinglong.github.io/myBlog/
MIT License
7 stars 0 forks source link

js关键词之this #74

Open zhaobinglong opened 3 years ago

zhaobinglong commented 3 years ago

this的本质

this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

this的指向

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象

this指向的四种情况

a.如果是一般函数,this指向全局对象window; b.在严格模式下"use strict",为undefined. c.对象的方法里调用,this指向调用该方法的对象. d.构造函数里的this,指向创建出来的实例.

实例1

function a(){
    var user = "追梦子";
    console.log(this.user); //undefined
    console.log(this); //Window
}
a();

var o = {
    user:"追梦子",
    fn:function(){
        console.log(this.user);  //追梦子
    }
}
o.fn();

var o = {
    a:10,
    b:{
        // a:12,
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
// 多重掉套嵌套调用的情况下,this指向的也只是它上一级的对象
o.b.fn();

参考

https://www.cnblogs.com/pssp/p/5216085.html http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

zhaobinglong commented 3 years ago

new操作符改变this

为什么this会指向a?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

var user = 'kkk'
function Fn(){
    this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子

原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就 是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要

new过程

1、创建一个空对象 obj 2、将该对象 obj 的原型链 proto 指向构造函数的原型 prototype, 并且在原型链 proto 上设置 构造函数 constructor 为要实例化的 Fn 3、传入参数,并让 构造函数 Fn 改变指向到 obj,并执行 4、最后返回 obj

实现new代码

function myNew(Fn) {
    let obj = {}    // 新建空对象
    let arg = Array.prototype.slice.call(arguments, 1)  // 取出参数,从第二个开始,转为数组
    obj.__proto__ = Fn.prototype      // 新对象的原型属性指向旧对象的原型对象
    obj.__proto__.constructor = Fn   
    let ret = Fn.apply(obj, arg)         // 执行一下
    return typeof ret === 'object' ? ret || obj : obj
}

造函数其实和普通函数

本质上并无区别,唯一的区别有两个:

zhaobinglong commented 3 years ago

箭头函数中的this

本质上,箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。

//换成箭头函数
let obj={
    name:"kk",
    fn:function(){
        setTimeout(()=>{console.log(this.name)});
    }
}
obj.fn(); // kk

let obj={
    name:"kk",
    fn:function(){
        setTimeout(function(){console.log(this.name)});
    }
}
obj.fn(); // undefined

箭头函数没有自己的this值,它的this值来自其所在的词法作用域

参考

https://zhuanlan.zhihu.com/p/57204184

zhaobinglong commented 3 years ago

手动改变this指向

模拟实现call

Function.prototype.call_ = function (obj) {
    var args = [];
    // 注意i从1开始
    for (var i = 1, len = arguments.length; i < len; i++) {
        args.push("arguments[" + i + "]");
    };
    obj.fn = this; // 此时this就是函数fn
    eval("obj.fn(" + args + ")"); // 执行fn
    delete obj.fn; //删除fn
};

模拟apply方法

Function.prototype.apply_ = function (obj, arr) {
    obj = obj ? Object(obj) : window;
    obj.fn = this;
    if (!arr) {
        obj.fn();
    } else {
        var args = [];
        // 注意这里的i从0开始
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push("arr[" + i + "]");
        };
        eval("obj.fn(" + args + ")"); // 执行fn
    };
    delete obj.fn; //删除fn
};

模拟实现bind

参考

https://segmentfault.com/a/1190000020044435

zhaobinglong commented 3 years ago

call(null)指向windows

function fun() {
    alert(this);
}
fun.call(null); // window or global