JavaScript基础--this解析

this关键字是javascript中最复杂的机制之一。它是一个很特别的关键字,被自动定义在全部函数的做用域中。
this既不指向函数自己也不指向函数的语法做用域。
this是在函数被调用时发生的绑定,this的绑定和函数声明的位置没有任何关系,它指向什么彻底取决于函数在哪里被调用。javascript

调用位置:是函数在代码中被调用的位置,而不是声明的位置。

this的4条绑定规则

* 默认绑定
* 隐式绑定
* 显式绑定
* new绑定

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

没法应用其它规则时的默认规则
若是使用严格模式(strict mode),则不能将全局对象用于默认绑定,所以this会绑定到undefined。
最经常使用的函数调用类型:独立函数调用。java

function foo(){
    console.log(this.a)
}
var a = 2;
foo() //2  >函数调用时应用了this的默认绑定,所以this指向全局对象

'tips: 声明在全局做用域中的变量就是全局对象的同名属性。'

//严格模式 example 1
function (){
    "use strict";
    console.log(this.a)
}
var a = 2;
foo(); //TypeError:this is undefined

//严格模式 example 2
function foo(){
    console.log(this.a)
}
var a = 2;
(function(){
    "use strict";
    foo(); //2
})();
隐式绑定

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

//example 1:
function foo(){
    console.log(this.a)
}
var obj = {
    a: 2,
    foo: foo
}
obj.foo();//2

//example 2:
var obj2 = {
    a: 42,
    foo: foo
}
var obj1 = {
    a: 2,
    obj2: obj2
}
obj1.obj2.foo();//42
显式绑定

直接指定this的绑定对象称为显式绑定
常见的显式绑定方法有 call()apply()编程

call和apply的使用方法:

function.call(thisArg, arg1, arg2, ...)

//thisArg           在 f unction 函数运行时使用的 this 值

//arg1, arg2, ...   指定的参数列表。


func.apply(thisArg, [argsArray]) 

//thisArg    在 f unc 函数运行时使用的this值,
             //若是这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

//argsArray  一个数组或者类数组对象,其中的数组元素将做为单独的参数传给'func'函数,
            //若是该参数的值为 null 或 undefined,则表示不须要传入任何参数。
            
            
两者区别
call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。
function foo(){
    console.log(this.a)
}
var obj={
    a: 2
}
foo.call(obj); //2 调用foo时强制把它的this绑定到obj上

装箱:若是你传入了一个原始值(字符串类型、布尔类型或数字类型)来看成this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(...)、new Boolean(...)或new Number(...))segmentfault

1>硬绑定
//硬绑定--显式的强制绑定
function foo(){
    console.log(this.a)
}
var obj = {
    a:2
}
var bar = function(){
    foo.call(obj)
}
bar(); //2

//硬绑定--硬绑定的使用场景就是建立一个包裹函数,负责接收参数并返回值
function foo(something){
    console.log(this.a,something);
    return this.a + something;
}
var obj={
    a: 2
}
var bar=function(){
    return foo.apply(obj,arguments)
}
var b = bar(3); //2 3
console.log(b); //5

//硬绑定--另外一种使用方法是建立一个能够重复使用的辅助函数
function foo(something){
    console.log(this.a,something)
    return this.a + something
}
//简单的辅助绑定函数
function bind(fn,obj){
    return function(){
        return fn.apply(obj,arguments)
    }
}
var obj = {
    a:2
}
var bar = bind(foo,obj);
var b = bar(3) //2 3
console.log(b) //5

//因为硬绑定是一种很是经常使用的模式,因此ES5提供了内置的方法Function.prototype.bind
function foo(something){
    console.log(this.a,something)
    return this.a+something
}
var obj={
    a:2
}
var bar = foo.bind(obj); //bind(...)会返回一个硬编码的新函数,它会把你指定的参数设置为this的上下文并调用原始函数
var b=bar(3); //2 3
console.log(b); //5

bind(...)的功能之一就是能够把除了第一个参数(第一个参数用于绑定this)以外的其余参数都传给下一层的函数


2>API调用的“上下文”
第三方库的许多函数,以及Javascript语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,一般被称为“上下文”(context),其做用和bind(...)同样,确保你的回调函数使用指定的this。
new绑定

在Javascript中,构造函数只是一些使用new操做符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。它们只是被new操做符调用的普通函数。
实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。数组

function foo(a){
    this.a = a
}
var bar = new foo(2)
console.log(bar); //2

使用new调用函数,或者说发生构造函数调用时,会自动执行下面的操做:
一、建立(或者说构造)一个全新的对象
二、这个新对象会被执行[[Prototype]]链接
三、这个新对象会绑定到函数调用的this
四、若是函数没有返回其余对象,那么new表达式中的函数调用会自动返回这个新对象安全

绑定例外

一、若是你把null或者undefined做为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
一种安全的作法是传入一个特殊对象,把this绑定到这个对象不会对你的程序产生任何反作用。
在Javascript中建立一个空对象最简单的方法都是Object。create(null)。Object。create(null)和{}很像,可是并不会建立Object.prototype这个委托,因此它比{}更空。app

二、有可能建立一个函数的“间接引用”,在这中状况下,调用这个函数会应用默认绑定规则。编程语言

function foo(){
    console.log(this.a)
}
var a =2
var o = {
    a: 3,
    foo: foo 
}
var p = {
    a: 4
}
0.foo(); //3
(p.foo = o.foo)(); //2   >>p.foo = o.foo的返回是目标函数的引用,所以调用位置是foo()而不是p.foo()或者o.foo()

三、软绑定 Function.prototype.softBind函数

if(!Function.prototype.softBind){
    Function.prototype.softBind = function(obj){
        var fn = this;
        //捕获全部的 curried 参数
        var curried = [].slice.call(arguments,1);
        var bound = function(){
            return fn.apply(
                (!this || this === (window || global)) ? 
                    obj : this,
                curried.concat.apply(curried,arguments)
            )
        }
        bound.prototype = Object.create(fn.prototype)
        return bound
    }
}

箭头函数

以前介绍的4条规则能够包含全部正常的函数,可是在es6中介绍了一种没法使用这些规则的特殊函数:箭头函数。

  • 箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)做用域来决定this。
  • 箭头函数的绑定没法被修改。(new也不行)
  • 箭头函数会继承外层函数调用的this绑定(不管this绑定到什么)。这其实和ES6以前代码中的self=this机制同样。

小知识点

柯里化

即Currying的音译。Currying是编译原理层面实现多参函数的一个技术。
Currying 为实现多参函数提供了一个递归降解的实现思路——把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数,在某些编程语言中(如 Haskell),是经过 Currying 技术支持多参函数这一语言特性的。

判断this绑定

先找到这个函数的直接调用位置,而后顺序应用下面这4条规则判断this的绑定对象:

  • 由new调用?绑定到新建立的对象。
  • 由call或apply或bind调用?绑定到指定的对象。
  • 由上下文对象调用?绑定到那个上下文对象。
  • 默认:在严格模式下绑定到undefined,不然绑定到全局对象。

文章摘取来源:《你不知道的JavaScript上卷》
 
 

更多文章
JavaScript基础--做用域与变量提高

相关文章
相关标签/搜索