晴明的博客园 GitHub      CodePen      CodeWars     

[js] this (1)

this是在运行时进行绑定的,并不是在编写时绑定的。
this的绑定和函数声明的位置没有关系,只取决于函数的调用方式。

四种标准规则
默认绑定
隐式绑定
显式绑定
new绑定

1

    var me = {
        name: "husky"
    }
    var you = {
        name: "reader"
    }

使用 this

    function identify() {
        return this.name.toUpperCase();
    }
    function speak() {
        var greeting = "hello, I am " + identify.call(this);
        console.log(greeting);
    }

    identify.call(me); //HUSKY
    identify.call(you); //READER
    speak.call(me); // hello, I am HUSKY
    speak.call(you); // hello, I am READER

不使用this

    function identify(cxt) {
        return cxt.name.toUpperCase();
    }
    function speak(cxt) {
        var greeting = "hello, I am " + identify(cxt);
        console.log(greeting);
    }
    identify(you); //READER
    speak(me); //hello, I am HUSKY

2

this并非指向函数自身

	  function foo(num){
          console.log("foo: " + num);
          this.count++; //记录foo被调用的次数
      }
      foo.count = 0;
      
      for(var i=0; i<10; i++){
          if(i > 5){
              foo(i)
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      
       console.log(foo.count); // 0 

词法作用域

	  function foo(num){
          console.log("foo: " + num);
          data.count++; //记录foo被调用的次数
      }
      var data ={
       count: 0
      };
      
      for(var i=0; i<10; i++){
          if(i > 5){
              foo(i);
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      console.log(data.count);// 4

词法标识符

    function foo(num){
          console.log("foo: " + num);
          foo.count++; // foo指向它自身
      }
      foo.count = 0;
      for(var i=0; i<10; i++){
          if(i > 5){
              foo(i);
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      console.log(foo.count);// 4	  

this

function foo(num){
          console.log("foo: " + num);
          this.count++;
      }
      foo.count = 0;
      for(var i=0; i<10; i++){
          if(i > 5){
              foo.call(foo, i); //使用call()可以确保this指向函数本身
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      console.log(foo.count);// 4	  

3

this在任何情况下都不指向函数的作用域 (?)

    function foo() {
        var a = 2;
        this.bar();
    }
    function bar() {
        console.log(this.a);
    }
    foo(); //ReferenceError: a is not defined

4

调用栈


    function baz() {
        //当前调用栈是:baz
        //因此,当前调用位置是全局作用域
        console.log("baz");
        bar(); //bar的调用位置
    }
    function bar() {
        //当前调用栈是:baz -> bar
        //因此,当前调用位置是在baz中
        console.log("bar");
        foo(); //foo的调用位置
    }
    function foo() {
        //当前调用栈是:baz -> bar -> foo
        //因此,当前调用位置是在baz中
        console.log("foo");
    }
    baz(); // baz的调用位置

5

默认绑定

    function foo() {
        console.log(this.a);//这里的 this 指向 window
    }
    var a = 2;
    foo(); // 2

6

隐式绑定

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

隐式绑定,对象属性引用链中只有最后一层在调用位置起作用

    function foo(){
        console.log(this.a);
    }
    var obj2 = {
        a: 100,
        foo: foo
    };
    var obj1 = {
        a: 1,
        obj2: obj2
    }
    obj1.obj2.foo(); // 100

隐式绑定的函数会丢失绑定对象

    function foo(){
        console.log(this.a);
    }
    var obj ={
        a: 2,
        foo: foo
    }
    var bar = obj.foo; //函数别名
    var a = "global";
    bar(); // "global"
    function foo(){
        console.log(this.a);
    }
    function callBack(fn){
        fn();
    }

    var obj = {
        a: 2,
        foo: foo
    }
    var a = "global";
    callBack(obj.foo); // "global"

参数传递其实就是一种隐式赋值

    var a = 1;
    function fn(){
        console.log(a); //1
        a = 2;
    }
    fn();
    console.log(a); // 2

    var a = 1;
    function fn(a){
        console.log(a); //undifined
        a = 2;
    }
    fn();
    console.log(a); // 1
function foo(){
    console.log(this.a)
}
var obj = {
    a: 2,
    foo: foo
}
var a = "global";
setTimeout(obj.foo, 1000); //"global"

JavaSceipt环境中内置的setTimeout()函数实现和下面的伪代码类似

function setTimeout(fn, delay){
    //等待delay秒
    fn(); //调用位置
}

7

显式绑定

call(绑定this, "参数1","参数2","参数3","参数4");
apply(绑定this, ["参数1","参数2","参数3","参数4"]);
function foo(){
    console.log(this.a);
}
var obj = {
    a: 2
}
foo.call(obj);//2

显式绑定-硬绑定

function foo(){
    console.log(this.a);
}
var obj = {
    a: 2
}
var bar = function(){
    foo.call(obj);
}
bar();// 2
setTimeout(bar, 100); // 2
bar.call(window); // 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);//将这里的this指向了 obj
}
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

Function.prototype.bind

function foo(something){
    console.log(this.a, something);
    return this.a + something;
}
var obj = {
    a:2
}
var bar = foo.bind(obj);
var b = bar(3); //2, 3
console.log(b); //5

8

new 绑定

function foo(a){
    this.a = a
}

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

9

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

function foo(){
    //返回一个箭头函数
    return (a) =>{
        //this继承自foo()
        console.log(this.a);
    }
}
var obj1 = {
    a: 2
}
var obj2 = {
    a: 3
}
var bar = foo.call(obj1);
bar.call(obj2); //2  不是3

箭头函数可以像bind(..)一样确保函数的this被绑定到指定的对象

function foo(){
    setTimeout(()=>{
        //这里的this在词法上继承自foo(),也就是说只要foo()绑定到了obj1上,箭头函数的this也就绑定到了obj1上
        console.log(this.a)
},100)
}
var obj1 = {
    a: 2
}
foo.call(obj1); //2

ES6之前就已经使的用一种几乎和箭头函数完全一样的模式

function foo(){
    console.log(this); //Object {a: 2}

    var self = this; //词法作用域捕获this

    setTimeout(function(){
        console.log(this); // Window {external: Object, chrome: Object, document: document, obj1: Object, obj2: Object…}

        console.log(self.a);
    }, 100);
}
var obj1 = {
    a: 2
}
foo.call(obj1); //2
posted @ 2016-09-08 10:40  晴明桑  阅读(198)  评论(0编辑  收藏  举报