换个思路清楚Javascript中的this,的七种采用情形

换个思路清楚Javascript中的this

2017/07/27 · JavaScript
· this

原文出处: Leechikit   

在网上广大篇章都对 Javascript 中的 this
做了详细的介绍,但基本上是介绍种种绑定格局或调用方式下 this
的对准,于是自己想有一个统一的思绪来更好驾驭 this
指向,使咱们更好判断,以下有部分内容不是规律,而是一种解题思路。

Javascript 中的
this,有时候令人迷惑,所以总括了眨眼间间关于this指向的题目。

1. 大局代码中的this

      this在大局上下文中,它的值是全局对象自我(Global
Object),在浏览器中就是Window  Object,如下图示。

      亚洲必赢官网 1

      看上边几个例子:

//Global scope
//The implicit property of the global object 
var foo1 = “abc”;
console.log(this.foo1 == window.foo1); console.log(foo1); console.log(this.foo1); console.log(window.foo1);  //true abc abc abc
 
//The implicit property of the global object 
foo2 = “def”;  
console.log(this.foo2 == window.foo2); console.log(foo2); console.log(this.foo2); console.log(window.foo2);      //true def def def
 
//The explicit property of the global object 
this.foo3 = “gh”;  
console.log(this.foo3 == window.foo3); console.log(foo3); console.log(this.foo3); console.log(window.foo3); //true gh gh gh

 

js 基础

  1. var val = (1,2,4)
    val //4
  2. var obj = {}
    Object.defineProperty(obj, ‘x'{
    configurable:false,
    value:1
    })
    delete obj.x //false
    obj.x //1

3.in

window.x = "1"
"x" in window //true
  1. try {
    throw “test”;
    } catch (ex) {
    console.log(ex); // test
    } finally {
    console.log(‘finally’);
    }
  2. ‘use strict’
    严苛格局是一种特其他实践情势,
    它修复了一部分语言上的供不应求,提供更强的不当检查,并加强安全性。
    不允许用with所有变量必须注脚,
    赋值给为申明的变量报错,而不是隐式创设全局变量。
    eval中的代码无法成立eval所在功效域下的变量、函数。而是为eval单独创设一个成效域,并在eval再次回到时抛弃。
    函数中得分外目的arguments是静态副本,而不像非严加情势那样,修改arguments或涂改参数变量会相互影响。
    删除configurable=false的性质时报错,而不是忽视
    禁绝八进制字面量,如010 (八进制的8)
    eval, arguments变为关键字,不可作为变量名、函数名等
    相似函数调用时(不是目的的艺术调用,也不行使apply/call/bind等修改this)this指向null,而不是大局对象。
    若选取apply/call,当传入null或undefined时,this将本着null或undefined,而不是大局对象。
    精算修改不可写属性(writable=false),在不可扩充的对象上添加属性时报TypeError,而不是忽视。
    arguments.caller, arguments.callee被禁用

prototype //直接评释的函数 拥有prototype这一个特性,而new
构造出来的函数不存在prototype这么些属性象。

function foo(){}                                function foo2(){}
undefined                                       undefined
foo.x = 1                                       foo2.prototype.x = 1
1                                               1
foo.y=2                                         foo2.prototype.y = 1
2                             VS                1
var test = new foo()                            var test2= new foo2()
undefined                                       undefined
test2                                           test2
foo {}                                          foo2 {x: 1, y: 1}
test.x                                          test2.x
undefined                                       1
  1. 对象创立

    var a ={} var b = Object.create({x:1})
    undefined undefined
    a.x =1 b.x
    1 1
    a.hasOwnProperty(‘x’) b.hasOwnProperty(‘x’)
    true false

  2. 对象属性删除

    var a = {}
    a.x = 1
    delete a.x`

    var globalVal = 1;
    delete globalVal; // false

    (function() {
    var localVal = 1;
    return delete localVal;
    }()); // false

    ohNo = 1;
    window.ohNo; // 1
    delete ohNo; // true

  3. 对象属性检测
    in hasOwnProperty propertyIsEnumerable

  4. 枚举类型

    var o = {x : 1, y : 2, z : 3};
    for (var key in o) {
    console.log(key); // x, y, z
    }

  5. getter setter
    var man = {
    name : ‘Bosn’,
    weibo : ‘@Bosn’,
    get age() {
    return new Date().getFullYear() – 1988;
    },
    set age(val) {
    console.log(‘Age can’t be set to ‘ + val);
    }
    }
    console.log(man.age); // 27
    man.age = 100; // Age can’t be set to 100
    console.log(man.age); // still 27
    var man = {
    weibo : ‘@Bosn’,
    $age : null,
    get age() {
    if (this.$age == undefined) {
    return new Date().getFullYear() – 1988;
    } else {
    return this.$age;
    }
    },
    set age(val) {
    val = +val;
    if (!isNaN(val) && val > 0 && val < 150) {
    this.$age = +val;
    } else {
    throw new Error(‘Incorrect val = ‘ + val);
    }
    }
    }

对象属性及的权杖设置

       var person = {};
       Object.defineProperty(person, 'name', {
       configurable : false, //配置
        writable : false,   //写
       enumerable : true, //枚举
       value : "Bosn Ma" //值
       });
       person.name; // Bosn Ma
   person.name = 1;
   person.name; // still Bosn Ma
   delete person.name; // false
        Object.defineProperties(person, {
    title : {value : 'fe', enumerable : true},
    corp : {value : 'BABA', enumerable : true},
    salary : {value : 50000, enumerable : true, writable : true}
});
Object.getOwnPropertyDescriptor(person, 'salary');
// Object {value: 50000, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(person, 'corp');
// Object {value: "BABA", writable: false, enumerable: true, configurable: false}`
  1. call apply
    1、方法定义
    call方法:
    语法:call([thisObj[,arg1[, arg2[,[,.argN]]]]])
    概念:调用一个对象的一个措施,以另一个目的替换当前目标。
    说明:

    call 方法可以用来代替另一个目的调用一个办法。call
    方法可将一个函数的靶子上下文从发轫的上下文改变为由 thisObj
    指定的新目的。
    若果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

    apply方法:
    语法:apply([thisObj[,argArray]])
    概念:应用某一目标的一个措施,用另一个对象替换当前目标。
    说明:
    如若 argArray 不是一个实用的数组或者不是 arguments
    对象,那么将招致一个 TypeError。
    假如没有提供 argArray 和 thisObj 任何一个参数,那么 Global
    对象将被当作 thisObj, 并且无法被传送任何参数.

  2. function bind
    this.x = 9
    9
    var module = {
    x:81,
    getX: function(){
    return this.x
    }
    }
    module.getX()
    81
    var getX = module.getX
    getX()
    9
    var boundGetX = getX.bind(module)
    boundGetX()
    81
    function add(a,b,c){
    return a + b + c
    }
    var func =add.bind(undefined, 100)
    func(1,2)
    103
    var func2 =func.bind(undefined, 200)
    func2(100)
    400
    function getConfig(color, size, otherOptions){
    console.log(color, size, otherOptions)
    }
    var defaultConfig = getConfig.bind(null, “#000”, “1024 * 768”)`
    defaultConfig(“123”)
    #000 1024 * 768 123
    defaultConfig(“456”)
    #000 1024 * 768 456

  3. 闭包 //允许一个函数在马上词法作用域外调用时,仍是可以访问当地变量
    优点: //灵活、 方便
    缺点: //浪费空间、内存败露、性能消耗

  4. arguments caller callee
    arguments :
    // 在函数调用时, 会自动在该函数内部生成一个名为
    arguments的隐蔽对象。 该对象类似于数组,
    //但又不是数组。可以接纳[]操作符获取函数调用时传递的实参。

    caller :
    //在一个函数调用另一个函数时,被调用函数会自动生成一个caller属性,指向调用它的函数对象。
    //如若该函数当前未被调用,或并非被此外函数调用,则caller为null。

    callee :
    //当函数被调用时,它的arguments.callee对象就会指向自身,也就是一个对团结的引用。
    //由于arguments在函数被调用时才使得,因而arguments.callee在函数未调用时是不存在的(即null.callee),且解引用它会爆发相当。
    //

  5. this

    在函数执行时,this 总是指向调用该函数的目的。要判断 this
    的针对,其实就是判断 this 所在的函数属于什么人。

    在《javaScript语言漂亮》那本书中,把 this
    出现的景色分为四类,简单来说就是:

    有对象就本着调用对象
    没调用对象就对准全局对象
    用new构培育本着新目标
    通过 apply 或 call 或 bind 来改变 this 的所指。
    1)函数有所属对象时:指向所属对象

    函数有所属对象时,平时通过 . 表达式调用,那时 this
    自然指向所属对象。比如上边的例证:

    var myObject = {value: 100};
    myObject.getValue = function () {
    console.log(this.value); // 输出 100

    // 输出 { value: 100, getValue: [Function] },
    // 其实就是 myObject 对象自我
    console.log(this);

    return this.value;
    };

    console.log(myObject.getValue()); // => 100
    getValue() 属于对象 myObject,并由 myOjbect 举办 . 调用,由此 this
    指向对象 myObject。

    1. 函数没有所属对象:指向全局对象

    var myObject = {value: 100};
    myObject.getValue = function () {
    var foo = function () {
    console.log(this.value) // => undefined
    console.log(this);// 输出全局对象 global
    };

    foo();

    return this.value;
    };

    console.log(myObject.getValue()); // => 100
    在上述代码块中,foo 函数固然定义在 getValue
    的函数体内,但实质上它既不属于 getValue 也不属于 myObject。foo
    并从未被绑定在其他对象上,所以当调用时,它的 this 指针指向了全局对象
    global。

    传闻那是个安排不当。

    3)构造器中的 this:指向新对象

    js 中,大家透过 new 关键词来调用构造函数,此时 this
    会绑定在该新对象上。

    var SomeClass = function(){
    this.value = 100;
    }

    var myCreate = new SomeClass();

    console.log(myCreate.value); // 输出100
    附带说一句,在 js
    中,构造函数、普通函数、对象方法、闭包,那四者没有强烈界线。界线都在人的心中。

    1. apply 和 call 调用以及 bind 绑定:指向绑定的目的

    apply()
    方法接受八个参数第二个是函数运行的功效域,此外一个是一个参数数组(arguments)。

    call() 方法第四个参数的意思与 apply()
    方法同样,只是其余的参数要求一个个列举出来。

    粗略来说,call 的章程更就像我们平日调用函数,而 apply 需求大家传递
    Array 方式的数组给它。它们是能够互相转换的。

    var myObject = {value: 100};

    var foo = function(){
    console.log(this);
    };

    foo(); // 全局变量 global
    foo.apply(myObject); // { value: 100 }
    foo.call(myObject); // { value: 100 }

    var newFoo = foo.bind(myObject);
    newFoo(); // { value: 100 }

  6. 换个思路清楚Javascript中的this,的七种采用情形。闭包的一个坑
    闭包这一个概念,在函数式编程里很宽泛,简而言之,就是使内部函数可以访问定义在表面函数中的变量。
    for (var i = 0; i < 5; i++) {
    setTimeout(function () {
    console.log(i);
    }, 5);
    }
    上边那么些代码块会打印多个 5 出来,而我辈预料的结果是打印 1 2 3 4
    5。
    因而会这么,是因为 set提姆eout 中的 i 是对外围 i 的引用。当
    set提姆(Tim)eout 的代码被分解的时候,运行时只是记录了 i
    的引用,而不是值。而当 set提姆eout 被触发时,四个 set提姆eout 中的 i
    同时被取值,由于它们都指向了外围的同一个 i,而丰硕 i
    的值在迭代已毕时为 5,所以打印了三回 5。
    为了拿走我们预料的结果,大家得以把 i
    赋值成一个局地的变量,从而摆脱外层迭代的影响。

    for (var i = 0; i < 5; i++) {
    (function (idx) {
    setTimeout(function () {
    console.log(idx);
    }, 5);
    })(i);
    }

从call方法开头

call 方法允许切换函数执行的上下文环境(context),即 this
绑定的靶子。

多数介绍 this 的篇章中都会把 call
方法放到最终介绍,但此文大家要把 call 方法放在首位介绍,并从
call 方法切入来商量 this ,因为 call 函数是显式绑定 this
的针对,大家来看望它怎么着模拟完毕(不考虑传入 nullundefined
和原始值):

Function.prototype.call = function(thisArg) { var context = thisArg; var
arr = []; var result; context.fn = this; for (let i = 1, len =
arguments.length; i < len; i++) { arr.push(‘arguments[‘ + i + ‘]’);
} result = eval(“context.fn(” + arr + “)”); delete context.fn; return
result; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.prototype.call = function(thisArg) {
    var context = thisArg;
    var arr = [];
    var result;
 
    context.fn = this;
 
    for (let i = 1, len = arguments.length; i < len; i++) {
        arr.push(‘arguments[‘ + i + ‘]’);
    }
 
    result = eval("context.fn(" + arr + ")");
 
    delete context.fn;
 
    return result;
}

从上述代码大家得以见到,把调用 call
方法的函数作为第二个参数对象的法门,此时一定于把第二个参数对象作为函数执行的上下文环境,而
this 是指向函数执行的上下文环境的,由此 this
就针对了第三个参数对象,达成了 call
方法切换函数执行上下文环境的效果。

在函数中 this
到底取何值,是在函数真正被调用执行的时候确定下来的,函数定义的时候确定不了。

2. 函数代码中的this

      共有四种函数调用格局:

     
1)方法调用格局:作为目标属性调用,obj.func();(详见8.对象中的this)

 
    2)函数调用情势:指向全局,直接调用func();     (详见1.全局代码中this)

      3)构造器调用格局:使用new调用;  
                (详见6.看作构造器调用的函数中的this)

 
    4)call/apply调用形式:动态改变this指向。        
(详见7.手动设置函数调用时this的值) 

 

     
函数代码中this值的率先个特色,同时也是最重大的风味,即:它并非静态地绑定在函数上。

      this的值是在进入实践上下文(Context
Excution)的阶段确定的,在函数代码中,其值每一回都不相同。但是进入实施代码阶段,其值就不可能更改。

     
假设想给this赋一个新值是不容许的,因为在当时this根本就不是变量了。

      结合例子来分析:

var foo = {
    name: “Foo”
};
var bar= {
    name: “ting”,
    sayName: function() {
        console.log(this ===
bar);
        console.log(“My name is: ” + this.name);
    }
};
bar.sayName();   //true My name is: ting
foo.sayName = bar.sayName;
foo.sayName();      //false My name is: Foo

     
通过上述结果分析可见:调用bar对象的sayName方法时,this指向bar对象。通过赋值格局使得foo的sayName方法指向bar的sayName方法,再调用foo对象的sayName方法时,this指向foo对象。Why?

     
this的值在函数中是非静态的,它的值确定是在举办代码阶段(函数调用时,具体代码执行前)。this的值是由激活上下文代码的调用者提供的,比如调用函数的外层上下文,更关键的是,this的值是由调用表明式的样式决定的。由此说,this并非静态绑定在函数上。

     
由于this非静态地绑定在函数上,那么大家是不是足以在函数中动态地修改this的值吗?

var foo = {
    name: “Foo”
};
var person = {
    name: “ting”,
    sayName: function() {
        console.log(this == person);
        this = foo;
        console.log(“My name is: ” + this.name);
    }
};
person.sayName();   //My name is Foo
foo.sayName = person.sayName;
foo.sayName();      //My name is ting

     
在sayName方法中,动态地修改this的值,重新履行以上的代码,发现this的值引用错误。那是由于一旦进入代码执行阶段(函数调用时,执行代码前),this的值确定,不可能被改变。

      影响函数代码中this值变化的因素:在平时的函数调用中,this是由激活上下文代码的调用者来提供的,即调用函数的父上下文(parent
context)。this值取决于调用函数的章程。 

     
误区:一些文章或JavaScript书籍中关系:this值取决于函数定义的方法,假如它是全局函数,this设置为大局对象,借使它是一个对象的办法,那么this总是指向那一个目的。那是纯属不得法的。

      切记一点:正是调用函数的章程影响了上下文中this的值。

      eg:
全局函数被调用情势的两样情势激活,分裂的调用格局导致了差距的this值。

function foo() {
    console.log(this);
}
foo();  //Window

console.log(foo == foo.prototype.constructor);  //true
foo.prototype.constructor();  //foo {}

      eg: 对象定义的办法,执行该方法,this值未必指向该对象。

var foo = {
    bar: function() {
        console.log(this);
        console.log(this === foo);
    }
};
foo.bar(); //foo true
var fooTest = foo.bar;
console.log(fooTest === foo.bar);
//同一个function,不一致的调用表明式,this值不一样
fooTest(); //Window false

      调用方式怎样影响this的值?
为充裕领悟this值的规定,需要详细分析其里面类型之一,即引用类型(Reference
type)。

 

目的方法中的this

在模拟 call 方法的时候,我们接纳了目的方法来改变 this
的针对性。调用对象中的方法时,会把目标作为艺术的上下文环境来调用。

既然 this
是指向执行函数的上下文环境的,那我们先来探讨一下调用函数时的实施上下文情形。

下边我门来探视调用对象方法时实行上下文是什么的:

var foo = { x : 1, getX: function(){ console.log(this.x); } }
foo.getX();

1
2
3
4
5
6
7
var foo = {
    x : 1,
    getX: function(){
        console.log(this.x);
    }
}
foo.getX();

亚洲必赢官网 2

从上图中,大家可以看来getX主意的调用者的上下文是foo,因此getX措施中的
this 指向调用者上下文foo,转换成 call
方法为foo.getX.call(foo)

上边大家把任何函数的调用形式都按调用对象方法的笔触来更换。

因为 this
的取值是函数执行上下文(context)的一有的,每便调用函数,都会生出一个新的履行上下文环境。当代码中利用了
this,那一个 this
的值就直接从执行的内外文中获取了,而不会从功能域链中追寻。

3. 引用类型(Reference Type)

     
使用伪代码将引用类型的值表示为所有八个特性的目的,包蕴base(拥有属性的不胜目的),对象中的propertyName(属性名)。

var valueofRefereceType = {
    base: <base object>,
    propertyName: <property name>
};

      引用类型的值唯有三种情状:

1)当大家处理一个标识符时

2)或性能访问器

     
标识符的处理进程另作商量,如今只需精通,该算法的再次来到值中,总是一个引用类型的值,那对于this来说更加重大。

     
标识符是:变量名、函数名、函数参数名或全局对象中未识其他属性名。例如,上面标识符的值:

var foo;
function bar() { } 

      在操作当中结果的经过中,引用类型对应的值如下:

var fooReference = {
    base: global,
    propertyName: ‘foo’
};

var barReference = {
    base: global,
    propertyName: ‘bar’
};

      伪代码GetValue描述从一个引用类型中拿走目标真正的值。

function GetValue(value) {
    if( Type(value) != Reference) {
        return value;
    }
    var base = GetBase(value);
    if(base === null) {
        throw return new ReferenceTypeError;
    }
    return base.[[Get]](GetPropertyName(value));
}

     
内部的[[Get]]方法再次回到对象属性真正的值,包蕴对原型链中继承的性能分析。

GetValue(fooReference); // 10
GetValue(barReference); // function object “bar”

      属性访问器:点(.)语法或括号([])语法

foo.bar();
foo[‘bar’]();

      在中游总计的重返值中,引用类型的值为:

var fooReference = {
    base: foo,
    propertyName: ‘bar’
};

GetValue(fooReference); // function object “bar”

   
  引用类型的值域函数上下文中的this怎么样相关?
–从最根本的意义上的话。

 本条关系的进度是那篇作品的主旨。

      一个函数上下文中,确定this值的通用规则如下:

1)在一个函数上下文中,this由调用者提供,由调用函数的法门来支配。

2)如若调用括号()的左边是援引类型的值,this值将设为引用类型值的base对象(base
object)。

3)假如调用括号()的右侧不是援引类型的值(与引用类型类型分裂的其余其他性质),this值为null,可是事实上不设有this值为null的情事,因为当this值为null的时候,其值会被隐示转换为大局对象。(注:第5版的ECMAScript,已经不强求转换为全局变量,而是赋值为undefined。) 

      看些例子,精通函数上下文中this值。

function foo() {
    console.log(this);
}
foo(); //window

      调用括号()的右边是引用类型的值,因为foo是一个标识符。

var fooReference = {
    base: global,
    propertyName: ‘foo’
};

      相应地,this设置为引用类型的base对象,即全局对象。

      使用性质访问器:

var foo = {
    bar: function() {
        console.log(this);
    }
};
foo.bar(); //foo

     
我们再度拥有一个引用类型,其base是foo对象,在函数bar激活时用作this。

var fooBarReference = {
    base: foo,
    propertyName: ‘bar’
};

      可是,使用此外一种样式激活相同的函数,大家赢得任何的this值。

var foo = {
  bar: function () {
    console.log(this);
  }
};
var test = foo.bar;
test(); 

//window

     
因为test作为标识符,生成了引用类型的其余值,其base(全局对象)用作this的值。

var testReference = {
    base: global,
    propertyName: ‘test’
};

     
从上述例子中可以分析得知:使用区其余表明式激活函数为什么会造成差其余this值,原因在于变动了引用类型(Type
Reference)分歧的中间值
。  

      再看四个例子,加深影象。

var foo = function() {
    console.log(this);
};
foo(); //window,because
var fooReference = {
    base: global,
    propertyName: ‘foo’
};

console.log(foo = foo.prototype.constructor);
//其余一种方式的调用表明式
foo.prototype.constructor(); //foo.prototype,because
var fooPrototypeConstructor = {
    base: foo.prototype,
    propertyName: ‘constructor’
};

      通过调用方式动态确定this值的经典例子。

function foo() {
    console.log(this.bar);
}
var x = {bar: 10};
var y = {bar: 20};
x.test = foo;
y.test = foo;

x.test(); //10
y.test(); //20

      

构造函数中的this

function Foo(){ this.x = 1; this.getX = function(){ console.log(this.x);
} } var foo = new Foo(); foo.getX();

1
2
3
4
5
6
7
8
function Foo(){
    this.x = 1;
    this.getX = function(){
        console.log(this.x);
    }
}
var foo = new Foo();
foo.getX();

执行 new
如果不考虑原型链,只考虑上下文的切换,就一定于先成立一个空的对象,然后把这一个空的目标作为构造函数的上下文,再去履行构造函数,最终回到那些目的。

var newMethod = function(func){ var context = {}; func.call(context);
return context; } function Foo(){ this.x = 1; this.getX = function(){
console.log(this.x); } } var foo = newMethod(Foo); foo.getX();

1
2
3
4
5
6
7
8
9
10
11
12
13
var newMethod = function(func){
    var context = {};
    func.call(context);
    return context;
}
function Foo(){
    this.x = 1;
    this.getX = function(){
        console.log(this.x);
    }
}
var foo = newMethod(Foo);
foo.getX();

有关 this 的取值,大体上可以分成以下七种状态:

4. 函数调用和非引用类型

     
正如前方所涉嫌的,当调用括号的左边不是引用类型而是其它项目,这一个值自动安装为null,结果为大局对象。

      思考如下那种表明式:

(function () {
    console.log(this);
})(); //null —> global

     
在那么些例子中,大家有一个函数对象但不是引用类型的目的(它不是标识符,也不是性质选拔器),相应地,this值最后设为全局对象。

      愈多复杂的例证:

var foo = {
    bar: function() {
        console.log(this);
    }
};
foo.bar(); //foo
(foo.bar)(); //foo

(foo.bar = foo.bar)(); //Window
(false || foo.bar)(); //Window
(foo.bar, foo.bar)(); //Window

     
一个特性访问器,它的中间值应该是援引类型的值,可是在好几调用中,大家取得的this值不是base对象,而是global对象。Why?

     
原因在于后边3个的调用中,应用一定的操作符之后,调用括号左侧的值不再是引用类型的值。

      第三个例子 — 调用括号右边是援引类型的值,this为base对象,即foo;

      第一个例子 —
组运算符并不适用,如下边所提到的,从引用类型获取一个目的真正值的章程,如GetValue。
相应地,在组运算的回到中,得到的仍是一个引用类型。由此this值再一次被设为base对象,即foo;

      第多个例子 —
与组操作符不相同,赋值运算符触发了GetValue方法,再次回到的结果是函数对象,而不是引用类型的值,那意味this值会被装置为null,最后会被改为全局对象。

      第二个例子 — 逻辑或运算符触发了GetValue方法,重返的结果……

      第七个例子 — 逗号运算符触发了GetValue方法,再次来到的结果是…… 

 

亚洲必赢官网 3

事态一:全局 & 调用日常函数

5. 引用类型和this为null

     
例外的事态:调用括号左边是援引类型的值,但this却为null,最后被隐示转换为全局对象(global
object)。情状建立的标准化是:当引用类型值的base对象恰好为活跃对象(activiation
object)。例如:里头子函数在父函数中被调用。一对变量、内部函数、格局参数存储在给定函数的激活对象中。 
 

function foo() {
  function bar() {
    console.log(this); // Window
  }
  bar(); // the same as AO.bar()
}

foo(); 

     
活跃对象总是作为this重返,值为null(用伪代码表示AO.bar()相当于null.bar())。正如上面例子提到的,this的值最后由null隐示转换为大局对象。

      上述例子的变形:

function foo() {
  function bar() {
    console.log(this); // Window
  }
  return bar(); // the same as AO.bar()
}
foo(); 

      有一种景况与上述情形又不平等:使用with。

     
要是with对象涵盖函数属性,并且在with语句块中调用该函数。with语句会将该目的添加到功用域链的最前边,即在外向对象往日。相应地,有了引用类型的值(标识符或性能访问器),其base对象不再是活泼对象,而是with语句对象。其它,值得一提的是,它不仅只针对内部函数,也针对全局函数,原因在于with对象比效能域链最前方的目的(全局对象或活跃对象)还要靠前。

var x =10;
with({
    foo: function() {
        console.log(this.x);
    },
    x: 20
}) {
    foo();  //20
}

//because
var fooReference = {
    base: _withObject,
    propertyName: ‘foo’
};

     
同样的情形出现在catch语句的实际参数中函数调用,catch对象添加到功能域的最前端,即移动目标或全局对象的眼前。然而,这么些一定的一举一动被认同为ECMAScript-262-3的bug,在ECMAScript-262-5中被修复了。那样,在特定的移位对象中,this不是指向catch对象,而是指向全局对象。

try {
    throw function() {
        console.log(this);
    };
} catch (e) {
    e();  //ES3规范里是_catchObject,ES5专业里是Window
}
//ES3
var eReference = {
    base: _catchObject,
    propertyName: ‘e’
};
//ES5
var eReference = {
    base: global,
    propertyName: ‘e’
};

     
同样的景况出现在命名函数的递归调用中。在首回递归调用中,base对象是父活跃对象或全局对象,在递归调用中,base对象应当是储存着函数表明式可选名称的特定对象。然则,在那种情景下,this总是指向全局对象。

(function foo(bar) {
  alert(this);
  !bar && foo(1);
})();

 

DOM事件处理函数中的this

DOMElement.addEventListener(‘click’, function(){ console.log(this); });

1
2
3
DOMElement.addEventListener(‘click’, function(){
    console.log(this);
});

把函数绑定到DOM事件时,可以作为在DOM上平添一个函数方法,当接触这么些事件时调用DOM上相应的风浪措施。

DOMElement.clickHandle = function(){ console.log(this); }
DOMElement.clickHandle();

1
2
3
4
DOMElement.clickHandle = function(){
    console.log(this);
}
DOMElement.clickHandle();

亚洲必赢官网 4

在全局环境中,this 永远指向 window。

6. 作为构造器调用的函数中的this

     
函数作为构造函数时,通过new操作符成立对象实例的历程包含:首先调用Foo()函数内部的[[construct]]主意,其次,在目标创制之后,调用内部的[[call]]艺术。所有同一的函数“Foo”将this设置为新创设的目的

function Foo() {
    console.log(this);
    this.x = 10;
}
var a = new Foo();
console.log(a.x);

     
再看一例:Person不做为构造函数调用,而是作为一般函数在全局功能域中被调用,this指向Window,由此全局对象中的name被修改为mars。至于Person.name的值为空字符串,有点奇怪?
更奇怪的是将代码中Person函数的name全体换为zzz或任何标识,Person.zzz为undefined??
原因是:name是函数的一个中间参数,标识函数的名字,由于赋给Person的函数为匿名函数,由此name为空字符串,可是用其余标识符,由于Person中从不概念该属性,由此值为undefined。

var name = ‘window_mars’;
var Person = function (name) {
    this.name = name;
};
Person(‘mars’);
console.log(name);         //mars
var s1 = Person.name;      //””
var o = new Person(“ting”);
console.log(o.name);       //ting

     
下边的例证可以很好地印证name属性为函数自带的属性值,且其值不能够被遮住。

var name = ‘window_mars’;
var Person = function (name) {
    this.name = name;
};
Person.name = name;
Person.name1 = name;
var s1 = Person.name;      //””
var s2 = Person.name1;     //”window_mars”
var o = new Person(“ting”);
console.log(o.name);       //ting

 

平凡函数中的this

var x = 1; function getX(){ console.log(this.x); } getX();

1
2
3
4
5
var x = 1;
function getX(){
    console.log(this.x);
}
getX();

那种状态下,大家创制一个虚构上下文对象,然后普通函数作为那个编造上下文对象的法门调用,此时见惯司空函数中的this就本着了这些编造上下文。

那这几个编造上下文是如何吧?在非严厉格局下是大局上下文,浏览器里是
window ,NodeJs里是 Global ;在严酷格局下是 undefined

var x = 1; function getX(){ console.log(this.x); } [viturl
context].getX = getX; [viturl context].getX();

1
2
3
4
5
6
7
var x = 1;
function getX(){
    console.log(this.x);
}
 
[viturl context].getX = getX;
[viturl context].getX();

亚洲必赢官网 5

console.log(this === window);    //true

7. 手动设置函数调用时this的值(apply()、call()) 

     
Function.prototype原型上定义了多少个主意,允许手动设置函数调用时的值,那七个章程分别是:apply()和call()。那八个措施都领受第二个参数作为调用上下文中this的值,而这五个方式的差异是传递参数,对于apply()方法,第一个参数是数组类型(或类数组对象,比如arguments),而对此call()方法接受任意多的参数(逗号分隔)。那五个点子的首先个参数要是不传或为null,那this的值为Window。

      例一: 

var b = 10;
function a(c) {
    console.log(this.b);
    console.log(c);
}
a(20); //this.b is 10, c is 20

a.apply({b:20},30); //this.b is 20, c is 30
b.apply({b:40},[40]); //this.b is 40, c is 40

      例二:

var myObject = {};
var myFunction = function(param1, param2) {
    this.x = param1;
    this.y = param2;
    console.log(this); //Object {x: “kkk”, y: “ooo”} 
};
myFunction.apply(myObject, [“kkk”, “ooo”]);
console.log(myObject); //Object {x: “kkk”, y: “ooo”} 
myFunction.call(myObject, “kkk”, “ooo”);
console.log(myObject); //Object {x: “kkk”, y: “ooo”} 

闭包中的this

var x = 1; var foo = { x: 2, y: 3, getXY: function(){ (function(){
console.log(“x:” + this.x); console.log(“y:” + this.y); })(); } }
foo.getXY();

1
2
3
4
5
6
7
8
9
10
11
12
var x = 1;
var foo = {
    x: 2,
    y: 3,
    getXY: function(){
        (function(){
            console.log("x:" + this.x);
            console.log("y:" + this.y);
        })();
    }
}
foo.getXY();

那段代码的上下文如下图:亚洲必赢官网 6

此间须求注意的是,大家再商讨函数中的 this 指向时,只要求关心
this 所在的函数是如何调用的, this
所在函数外的函数调用都是浮云,是不须求关注的。由此在拥有的图示中,大家只必要关切灰色框中的内容。

故此那段代码大家关怀的部分唯有:

(function(){ console.log(this.x); })();

1
2
3
(function(){
    console.log(this.x);
})();

与经常函数调用一样,创设一个虚拟上下文对象,然后普通函数作为那个编造上下文对象的艺术立即调用,匿名函数中的
this
也就本着了这几个虚拟上下文。亚洲必赢官网 7

平常函数在调用时候(注意不是构造函数,前边不加 new),其中的 this 也是指向
window。

8. 目标中的this

     
对象中this值的论断,其实说到底也牵涉到函数中this值的判断,因而可比照函数上下文中this值的论断规则来确定this值。
看上面多少个例子。

     
函数getName()上下文中this值的判断,引用类型中间值的base对象是person对象,this值为person对象,如上面代码所示。

var person = {
    name: “ting”,
    getName: function() {
        console.log(this); //Object {name: “ting”, getName: function}
        console.log(this.name); //ting
    }
};
person.getName();

     
函数getName()和getGlobalName()上下文中this值的论断,this值分别为person对象和大局对象。

var name = “global-ting”;
function getGlobalName () {
    return this.name;
}
var person = {
    name: “ting”,
    getName: function() {
        return this.name;
    }
};
var s1 = person.getName(); //”ting”
var s2 = getGlobalName();  //”global-ting”

      上述例子的变形:

var name = “global-ting”;
function getGlobalName () {
    return this.name;
}
var person = {
    name: “ting”,
    getName: getGlobalName
};
var s1 = person.getName(); //”ting”
var s2 = getGlobalName();  //”global-ting”

      上述例子的变形:

function getGlobalName () {
    return this.name;
}
var person = {
    name: “ting”,
    getName: getGlobalName
};
var hehe = {
    name: “haha”,
    getName: getGlobalName
}
var s1 = person.getName(); //”ting”
var s2 = getGlobalName();  //”global-ting”
var s3 = hehe.getName(); //”haha”

参数中的this

var x = 1; var foo = { x: 2, getX: function(){ console.log(this.x); } }
setTimeout(foo.getX, 1000);

1
2
3
4
5
6
7
8
var x = 1;
var foo = {
    x: 2,
    getX: function(){
        console.log(this.x);
    }
}
setTimeout(foo.getX, 1000);

函数参数是值传递的,因而地点代码等同于以下代码:

var getX = function(){ console.log(this.x); }; setTimeout(getX, 1000);

1
2
3
4
var getX = function(){
    console.log(this.x);
};
setTimeout(getX, 1000);

下一场大家又回到了常见函数调用的题目。

var x = 10;

9. DOM事件中的this

      示例一:el指向绑定该事件的dom元素,事件处理程序中的this指向Window

<script type=”text/javascript”>
        function test_this(el) {
            var element = el;
            var scope = this;
        }
</script>
<div id=”news” class=”overlay” onclick=”test_this(this);”>点击我</div>

亚洲必赢官网 8   

     
示例二:event指向触发的风云Mouse伊芙(Eve)nt,this指向绑定该事件的DOM元素。

<script type=”text/javascript”>
    function test_this(event) {
        var e = event;
        var scope = this;
    }
    document.getElementById(“news”).onclick = test_this;
</script>
<div id=”news” class=”overlay”>点击我</div>

亚洲必赢官网 9 
 亚洲必赢官网 10

     
示例三:与示例二的结果一致,即event指向事件Mouse伊夫nt,this为绑定该事件的DOM元素。

<script type=”text/javascript”>
    function test_this(event) {
        var e = evemt;
        var scope = this;
    }
    document.getElementById(“news”).addEventListener(“click”, test_this, false);
</script>
<div id=”news” class=”overlay”>点击我</div>

 

大局中的this

大局中的 this 指向全局的上下文

var x = 1; console.log(this.x);

1
2
var x = 1;
console.log(this.x);

亚洲必赢官网 11

function foo(){

10. 检测一下

     
exam1:baz为全局变量,函数执行完后,没有被假释;bar为一些变量,函数立即实施完,该变量被放出,由此报错。

(function () {
    baz = 5;
    var bar = 10;
})();
console.log(baz); //5

console.log(bar); //报错,ReferenceError:bar is not defined.

      exam2:严刻方式下会报错:ReferenceError:bar is not defined.

“use strict”;
(function () {
    baz = 5;   //报错
    var bar = 10;
})();
console.log(baz);

      exam3:两处的a都是在全局域中被操作。

亚洲必赢官网 ,a = 2;
var a = 1;
console.log(a); //1

 

      终于整理完了^-^.

 

 

 

时间:2014-10-19

地点:合肥 

引用:

        

        

 

复杂气象下的this

var x = 1; var a = { x: 2, b: function(){ return function(){ return
function foo(){ console.log(this.x); } } } }; (function(){ var x = 3;
a.b()()(); })();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var x = 1;
var a = {
    x: 2,
    b: function(){
        return function(){
            return function foo(){
                console.log(this.x);
            }        
        }
    }
};
 
(function(){
    var x = 3;
    a.b()()();
})();

观望地点的情形是有为数不少个函数,但我们只须求关注 this
所在函数的调用形式,首先大家来简化一下之类:

var x = 1; (function(){ var x = 3; var foo = function(){
console.log(this.x); } foo(); });

1
2
3
4
5
6
7
8
var x = 1;
(function(){
    var x = 3;
    var foo = function(){
        console.log(this.x);
    }
    foo();
});

this 所在的函数 foo
是个平凡函数,大家创立一个虚构上下文对象,然后普通函数作为那些虚拟上下文对象的办法马上调用。因而那么些
this本着了那么些虚拟上下文。在非严酷格局下是全局上下文,浏览器里是
window ,NodeJs里是 Global ;在严厉格局下是 undefined

console.log(this);    //Window

总结

在急需看清 this 的指向时,大家得以安装那种思路来驾驭:

  • 判断 this 在全局中OR函数中,若在全局中则 this
    指向全局,若在函数中则只关注那几个函数并继承判断。
  • 判断 this 所在函数是否作为对象方法调用,假诺则 this
    指向那么些目的,否则继续操作。
  • 创办一个虚构上下文,并把this所在函数作为那一个编造上下文的主意,此时
    this 指向这几个编造上下文。
  • 在非严谨方式下虚拟上下文是大局上下文,浏览器里是 window
    ,Node.js里是 Global ;在严格形式下是 undefined

图示如下:

亚洲必赢官网 12

 

1 赞 6 收藏
评论

亚洲必赢官网 13

console.log(this.x);  //10

}

foo();

场所二:构造函数

所谓的构造函数就是由一个函数 new
出来的目标,一般构造函数的函数名首字母大写,例如像
Object,Function,Array 这么些都属于构造函数。

function Foo(){

this.x = 10;

console.log(this);    //Foo {x:10}

}

var foo = new Foo();

console.log(foo.x);      //10

上述代码,即使函数作为构造函数使用,那么内部的 this 就表示它即将 new
出来的对象。

唯独如果直接调用 Foo 函数,而不是 new Foo(),那就成为景况1,那时候 Foo()
就变成寻常函数。

function Foo(){

this.x = 10;

console.log(this);    //Window

}

var foo = Foo();

console.log(foo.x);      //undefined

情况三:对象方法

假若函数作为目的的格局时,方法中的 this 指向该目的。

var obj = {

x: 10,

foo: function () {

console.log(this);        //Object

console.log(this.x);      //10

}

};

obj.foo();

瞩目:假如在目标方法中定义函数,那么情状就分歧了。

var obj = {

x: 10,

foo: function () {

function f(){

console.log(this);      //Window

console.log(this.x);    //undefined

}

f();

}

}

obj.foo();

可以如此清楚:函数 f 即使是在 obj.foo
内部定义的,但它照旧属于一个常见函数,this 仍指向 window。

在此处,若是想要调用上层效用域中的变量 obj.x,能够动用 self 缓存外部
this 变量。

var obj = {

x: 10,

foo: function () {

var self = this;

function f(){

console.log(self);      //{x: 10}

console.log(self.x);    //10

}

f();

}

}

obj.foo();

设若 foo 函数不作为对象方法被调用:

var obj = {

x: 10,

foo: function () {

console.log(this);      //Window

console.log(this.x);    //undefined

}

};

var fn = obj.foo;

fn();

obj.foo 被赋值给一个全局变量,并不曾当做 obj 的一个性质被调用,那么此时
this 的值是 window。

意况四:构造函数 prototype 属性

function Foo(){

this.x = 10;

}

Foo.prototype.getX = function () {

console.log(this);        //Foo {x: 10, getX: function}

console.log(this.x);      //10

}

var foo = new Foo();

foo.getX();

在 Foo.prototype.getX 函数中,this 指向的 foo
对象。不仅仅如此,尽管是在方方面面原型链中,this 代表的也是当前目标的值。

情况五:函数用 call、apply或者 bind 调用。

var obj = {

x: 10

}

function foo(){

console.log(this);    //{x: 10}

console.log(this.x);  //10

}

foo.call(obj);

foo.apply(obj);

foo.bind(obj)();

当一个函数被 call、apply 或者 bind 调用时,this 的值就取传入的对象的值。

情况六:DOM event this

在一个 HTML DOM 事件处理程序里,this 始终指向这么些处理程序所绑定的 HTML
DOM 节点:

function Listener(){

document.getElementById(‘foo’).add伊芙(Eve)ntListener(‘click’,
this.handleClick);    //那里的 this 指向 Listener
那几个目的。不是强调的是此处的 this

}

Listener.prototype.handleClick = function (event) {

console.log(this);    //

}

var listener = new Listener();

document.getElementById(‘foo’).click();

以此很好明白,就相当于是给函数传参,使 handleClick
运行时上下文改变了,相当于上面这样的代码:

var obj = {

x: 10,

fn: function() {

console.log(this);        //Window

console.log(this.x);      //undefined

}

};

function foo(fn) {

fn();

}

foo(obj.fn);

你也可以用经过 bind 切换上下文:

function  Listener(){

document.getElementById(‘foo’).addEventListener(‘click’,this.handleClick.bind(this));

}

Listener.prototype.handleClick = function (event) {

console.log(this);    //Listener {}

}

var listener = new Listener();

document.getElementById(‘foo’).click();

前六种景况实际上可以计算为: this 指向调用该措施的目的。

状态七:箭头函数中的 this

当使用箭头函数的时候,意况就截然差别了:箭头函数内部的 this
是词法功用域,由上下文确定。

var obj = {

x: 10,

foo: function() {

var fn = () => {

return () => {

return () => {

console.log(this);      //Object {x: 10}

console.log(this.x);    //10

}

}

}

fn()()();

}

}

obj.foo();

近日,箭头函数完全修复了 this 的对准,this
总是指向词法效能域,也就是外围调用者 obj。

假使使用箭头函数,此前的那种 hack 写法:

var self = this;

就不再须求了。

var obj = {

x: 10,

foo: function() {

var fn = () => {

return () => {

return () => {

console.log(this);    // Object {x: 10}

console.log(this.x);  //10

}

}

}

fn.bind({x: 14})()()();

fn.call({x: 14})()();

}

}

obj.foo();

鉴于 this 在箭头函数中曾经依据词法作用域绑定了,所以,用 call()或者
apply()调用箭头函数时,不可以对 this 进行绑定,即传入的首先个参数被忽略。

网站地图xml地图