Skip to main content

Command Palette

Search for a command to run...

This的四种绑定

Published
•1 min read•View as Markdown

this的绑定取决于它的调用位置,而不是取决于它的声明位置

function baz(){
    //调用栈baz,当前调用位置全局
    bar();
}
function bar(){
    //调用栈baz->bar,调用位置baz
    foo();
}
function foo(){
    //调用栈baz->bar->foo,调用位置bar
}
baz();

this的默认绑定

首先是最常用的函数调用类型:独立函数调用。这条规则是无法应用其他规则时的默认规则。

function foo(){
    console.log(this.a);
}
let a = 2;
foo();//2

foo()是直接使用不带任何修饰的函数引用进行调用,只能使用默认绑定。当中的this指向全局对象。但在严格模式下,则不能将全局对象用于默认绑定,this会绑定到undefined

this的隐式绑定

function foo(){
    console.log(this.a);
}
let obj2 = {
    a:42,
    foo:foo
}
let obj1 = {
    a:2,
    obj2:obj2
}
let a=3;
obj1.obj2.foo();//42

无论foo无论是在obj中定义还是先定义在添加为应用,这个函数严格来说都不属于obj对象。然而在调用位置使用obj上下文来引用对象时,可以说函数被调用时obj包含对函数的引用。

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。并且对象属性引用链只有最后一层调用位置起作用

隐式丢失

  • 函数别名

    let bar = obj2.foo;
    bar();//3
    
  • 间接引用

    let p = {a:4}
    obj2.foo();//42
    (p.foo = obj2.foo)();//3返回值是目标函数引用调用位置是foo()引用默认绑定
    
  • 回调函数

    setTimeout(obj2.foo,1000);
    

    参数传递的过程是一种隐式赋值

显式绑定

使用函数的call(...)、apply(...)来直接指定this的绑定对象。

function foo(){
    console.log(this.a);
}
let obj = {
    a:2
}
foo.call(obj);

使用bind解决隐式丢失(一些函数也有context选项确保指定的this)

function foo(something){
    return this.a + something;
}
let obj = {
    a:2
}
let a = 6;
let bar = foo.bind(obj);
let b = bar(3)//5

new绑定

js中的构造函数不属于某个类,也不会实例化一个类,它们只是被new操作符调用的普通函数

只用new调用函数会执行:

1. 构造一个全新的对象
2. 新对象会执行Prototype链接
3. 新对象会绑定到函数调用的this
4. 如果函数没有返回其他对象,new表达式中函数调用会自动返回这个新对象
function foo(a){
    this.a = a;
}
let bar = new foo(2);
console.log(bar.a);//2

优先级:new绑定>显示绑定>隐式绑定>默认绑定

箭头函数

箭头函数不使用this的四种标准规则,而是根据外层作用域决定。箭头函数的绑定无法修改

function foo(){
    return (a)=>{
        //this继承自foo()
        console.log(this.a)
    }
}
let obj1 = {a:2}
let obj2 = {a:3}
let bar = foo.call(obj1)
bar.call(obj2)//2,不是3
function foo(){
    setTimeout(()=>{
        console.log(this.a);
    },1000);
}
let obj = {a:2}
foo.call(obj);

More from this blog

Es5原型与继承

prototype:函数对象的原型对象(普通对象) __proto__:指向创建它的构造函数的原型对象 原型链的连接是实例(car1)和构造函数(Car)的原型对象(Car.prototype)之间,而不是实例和构造函数之间 构造函数式继承 可以子传父 引用类型的值相互独立 父类构造函数重复执行,每次创建子类实例都会调用一次 脱离于原型链,父类原型链上的方法属性无法继承 function Car(name,price){ this.name = name; this.pric...

Oct 16, 20211 min read

前端模块化与包管理器

什么是模块化? 将一个复杂的程序依据一定的规则封装成几个块文件,并进行组合。 模块化特点 模块中数据与实现是私有的,只向外部暴露一些接口方法与其他模块通信 模块化历程 全局function function util1(){ //... } function util2(){ //... } 引入js文件,这样会污染到全局。当时使用命名空间优化这个问题,引入一个工具对象(不私有) 闭包 向window对象添加全局属性 (function (win){ ...

Oct 15, 20213 min read

抽象语法树相关

抽象语法树 大多数编译器分为三个主要阶段:解析,转换和代码生成 解析正在获取原始代码并将其转换为更抽象的代码 代码的表示形式。 转换采取这种抽象表示并操纵去做 编译器想要的任何东西。 代码生成采用代码的转换表示形式, 将其转换为新代码。 解析 解析通常分为两个阶段:词法分析和句法分析。 词法分析提取原始代码并将其拆分为标记器(或词法分析器)的事物称为标记。 标记器是由微小的小物体组成的数组,描述了一个孤立的片断的语法。它们可以是数字,标签,标点符号,运算符,任何。 句法分...

Oct 15, 20215 min read
K

kc‘s blog

9 posts