• this的面面观


    http://www.cnblogs.com/Wayou/p/all-this.html
    《JavaScript语言精粹》
    全局this
    
    浏览器宿主的全局环境中,
    function f(xx) {         
        this.x = xx; 
    } 
    f(5); 
    x//5
    这个时候,函数 f 里的 this 绑定的是全局对象,如果是在浏览器运行的解释器中,一般来说是 window 对象。
    所以这里 this.x 访问的其实是 window.x ,当然,如果 window 没有 x 属性,那么你这么一写,按照 js 的坑爹语法,
    就是给 window 对象添加了一个 x 属性,同时赋值。
    
    var y=new f(10); 
    y.x;//10
    对于实例y this就是运行的这个函数
    上面这个函数和正常调用的函数写法上没什么区别,只不过在调用的时候函数名前面加了关键字 new罢了,这么一来,
    this 绑定的就不再是前面讲到的全局对象了,而是这里说的创建的新对象,所以说这种方式其实很危险,
    因为光看函数,你不会知道这个函数到底是准备拿来当构造函数用的,还是一般函数用的。
    
    bind() 函数 http://developer.51cto.com/art/201503/466978.htm
    https://msdn.microsoft.com/zh-cn/library/ff841995
    对于给定函数,创建具有与原始函数相同的主体的绑定函数。在绑定函数中,this 对象将解析为传入的对象。绑定函数具有指定的初始参数。
    
    function a(xx) {         
        this.b = xx; 
    } 
    var o = {}; 
    a.apply(o, [5]); 
    alert(a.b);    // undefined 
    alert(o.b);    // 5 
    
    上面讲的无论是 call() 也好, apply() 也好,都是立马就调用了对应的函数,而 bind() 不会, bind() 会生成一个新的函数,
    bind() 函数的参数跟 call() 一致,第一个参数也是绑定 this 的值,后面接受传递给函数的不定参数。 
    bind() 生成的新函数返回后,你想什么时候调就什么时候调,看下代码就明白了
    
    bind()会生成一个新的函数,执行新函数后, 那么它传入的主体就继承(不一定对,但是帮助理解)前者对象的属性了。如下测试
    
    function a(xx) {         
        this.b = xx; 
    } 
    var o = {}; var p = {};  var z={}; var d={};
    var km=a.bind(p,18);
    a.apply(o, [5]);
    o;//Object {b: 5}
    o.b//5
    var y=a.apply(z,[20]);
    y//undefined
    z//Object {b: 20};
    z.b//20
    var mm=a.call(d,100);
    mm;//undefined
    d:// Object {b: 100}
    d.b//100
    var km=a.bind(p,18);
    p;//Object {}
    km//bound a() { [native code] }
    km();//执行之后
    p;//Object {b: 18}
    p.b//18
    
    函数或方法里的this
    JavaScript原型
    原型就是相对实例后对象而言。如
    function test(){};
    var tp=new tes();
    tp的原型就是test
    同一函数创建的所有实例均共享一个原型。如果你给原型赋值了一个数组,
    那么所有实例都能获取到这个数组。除非你在某个实例中对其进行了重写,实事上是进行了覆盖。
    function Thing() {
    }
    Thing.prototype.things = [];
    
    var thing1 = new Thing();
    var thing2 = new Thing();
    thing1.things.push("foo");
    console.log(thing2.things); //logs ["foo"]
    通常上面的做法是不正确的(译注:改变thing1的同时也影响了thing2)。
    如果你想每个实例互不影响,那么请在函数里创建这些值,而不是在原型上。
    function Thing() {
        this.things = [];
    }
    
    var thing1 = new Thing();
    var thing2 = new Thing();
    thing1.things.push("foo");
    console.log(thing1.things); //logs ["foo"]
    console.log(thing2.things); //logs []
    
    
    多个函数可以形成原型链,这样this便会在原型链上逐步往上找直到找到你想引用的值。
    function Thing1() {
    }
    Thing1.prototype.foo = "bar";
    
    function Thing2() {
    }
    Thing2.prototype = new Thing1();
    
    var thing = new Thing2();
    console.log(thing.foo); //logs "bar"
    我习惯将赋值到原型上的函数称作方法。上面某些地方便使用了方法这样的字眼,比如logFoo方法。这些方法中的this同样具有在原
    型链上查找引用的魔力。通常将最初用来创建实例的函数称作构造函数。原型链方法中的this是从实例中的this开始住上查找整个
    原型链的。也就是说,如果原型链中某个地方直接对this进行赋值覆盖了某个变量,那么我们拿到的是覆盖后的值。
    function Thing1() {
    }
    Thing1.prototype.foo = "bar";
    Thing1.prototype.logFoo = function () {
        console.log(this.foo);
    }
    function Thing2() {
        this.foo = "foo";
    }
    Thing2.prototype = new Thing1();
    var thing = new Thing2();
    thing.logFoo(); //logs "foo";
    
    在JavaScript中,函数可以嵌套函数,也就是你可以在函数里面继续定义函数。但内层函数是通过闭包获取外层函数里定义的变量值的,而不是直接继承this。
    function Thing() {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = function () {
        var info = "attempting to log this.foo:";
        function doIt() {
            console.log(info, this.foo);
        }
        doIt();
    }
    
    var thing = new Thing();
    thing.logFoo();  //logs "attempting to log this.foo: undefined"
    上面示例中,doIt 函数中的this指代是全局作用域或者是undefined如果使用了"use strict";声明的话。对于很多新手来说,理解这点是非常头疼的。
    还有更奇葩的。把实例的方法作为参数传递时,实例是不会跟着过去的。也就是说,此时方法中的this在调用时指向的是全局this或者是undefined在声明了"use strict";时。
    function Thing() {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = function () {  
        console.log(this.foo);   
    }
    
    function doIt(method) {
        method();
    }
    
    
    var thing = new Thing();
    thing.logFoo(); //logs "bar"
    doIt(thing.logFoo); //logs undefined
    
    所以很多人习惯将this缓存起来,用个叫self或者其他什么的变量来保存,以将外层与内层的this区分开来。
    function Thing() {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = function () {
        var self = this;
        var info = "attempting to log this.foo:";
        function doIt() {
            console.log(info, self.foo);
        }
        doIt();
    }
    var thing = new Thing();
    thing.logFoo();  //logs "attempting to log this.foo: bar"
    ...但上面的方式不是万能的,在将方法做为参数传递时,就不起作用了。
    function Thing() {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = function () { 
        var self = this;
        function doIt() {
            console.log(self.foo);
        }
        doIt();
    }
    
    function doItIndirectly(method) {
        method();
    }
    var thing = new Thing();
    thing.logFoo(); //logs "bar"
    doItIndirectly(thing.logFoo); //logs undefined
    
    
    避免在构造函数中返回作何东西,因为返回的东西可能覆盖本来该返回的实例。
    function Thing() {
        return {};
    }
    Thing.prototype.foo = "bar";
    
    Thing.prototype.logFoo = function () {
        console.log(this.foo);
    }
    
    var thing = new Thing();
    thing.logFoo(); //Uncaught TypeError: undefined is not a function
    function Thing11() {
        return "888888";
    }
    Thing11.prototype.foo = "bar";
    
    
    Thing11.prototype.logFoo = function () {
        console.log(this.foo);
    }
    var thing12 = new Thing11();
    thing12.logFoo(); //bar
    但,如果你在构造函数里返回的是个原始值比如字符串或者数字什么的,上面的错误就不会发生了,返回语句将被忽略。
    所以最好别在一个将要通过new来调用的构造函数中返回作何东西,即使你是清醒的。如果你想实现工厂模式,那么请用一
    个函数来创建实例,并且不通过new来调用。当然这只是个人建议。
    诚然,你也可以使用Object.create从而避免使用new。这样也能创建一个实例。
    
    原型中的this
    function Thing() {
    }
    Thing.prototype.foo = "bar";
    
    Thing.prototype.logFoo = function () {
        console.log(this.foo);
    }
    var thing =  Object.create(Thing.prototype);
    thing.logFoo(); //logs "bar"
    
    正因为Object.create没有调用构造函数,这在当你想实现一个继承时是非常有用的,随后你可能想要重写构造函数。
    function Thing1() { 
        this.foo = "foo";
    }
    Thing1.prototype.foo = "bar";
    
    function Thing2() {
        this.logFoo(); //logs "bar" 可以理解
        Thing1.apply(this);//apply 可以使参数里面对象拥有前者的属性
        this.logFoo(); //logs "foo"
    }
    Thing2.prototype = Object.create(Thing1.prototype);
    Thing2.prototype.logFoo = function () {
        console.log(this.foo);
    }
    
    var thing = new Thing2();
    
    
    对象中的this
    可以在对象的任何方法中使用this来访问该对象的属性。这与用new得到的实例是不一样的。
    ar obj = {
        foo: "bar",
        logFoo: function () {
            console.log(this.foo);
        }
    };
    obj.logFoo(); //logs "bar"
    
    注意这里并没有使用new,也没有用Object.create,更没有函数的调用来创建对象。也可以将函数绑定到对象,就好像这个对象是一个实例一样。
    var obj = {
        foo: "bar"
    };
    
    function logFoo() {
        console.log(this.foo);
    }
    
    logFoo.apply(obj); //logs "bar"
    此时使用this没有向上查找原型链的复杂工序。通过this所拿到的只是该对象身上的属性而以。
    var obj = {
        foo: "bar",
        deeper: {
            logFoo: function () {
                console.log(this.foo);
            }
        }
    };
    
    obj.deeper.logFoo(); //logs undefined
    也可以不通过this,直接访问对象的属性。
    var obj = {
        foo: "bar",
        deeper: {
            logFoo: function () {
                console.log(obj.foo);
            }
        }
    };
    obj.deeper.logFoo(); //logs "bar"
    
    
    DOM 事件回调中的this
    在DOM事件的处理函数中,this指代的是被绑定该事件的DOM元素。
    function Listener() {
        document.getElementById("foo").addEventListener("click",
           this.handleClick);
    }
    Listener.prototype.handleClick = function (event) {
        console.log(this); //logs "<div id="foo"></div>"
    }
    
    var listener = new Listener();
    document.getElementById("foo").click();
    
    HTML中的this
    <div id="foo" onclick="console.log(this);"></div>
    <script type="text/javascript">
    document.getElementById("foo").click(); //logs <div id="foo"...
    </script>
    
    eval中的this
    eval 中也可以正确获取当前的 thisfunction Thing () {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = function () {
        eval("console.log(this.foo)"); //logs "bar"
    }
    
    var thing = new Thing();
    thing.logFoo();
    这里存在安全隐患。最好的办法就是避免使用eval。
    
    使用Function关键字创建的函数也可以获取this:
    
    function Thing () {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = new Function("console.log(this.foo);");
    var thing = new Thing();
    thing.logFoo(); //logs "bar"
    
    使用with时的this
    使用with可以将this人为添加到当前执行环境中而不需要显示地引用this。
    function Thing () {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = function () {
        with (this) {
            console.log(foo);
            foo = "foo";
        }
    }
    
    var thing = new Thing();
    thing.logFoo(); // logs "bar"
    console.log(thing.foo); // logs "foo"
    
    jQuery中的this
    一如HTML DOM元素的事件回调,jQuery库中大多地方的this也是指代的DOM元素。页面上的事件回调和一些便利的静态方法比如$.each 都是这样的。
    
    <div class="foo bar1"></div>
    <div class="foo bar2"></div>
    <script type="text/javascript">
    $(".foo").each(function () {
        console.log(this); //logs <div class="foo...
    });
    $(".foo").on("click", function () {
        console.log(this); //logs <div class="foo...
    });
    $(".foo").each(function () {
        this.click();
    });
    </script>
    
    传递 this
    如果你用过underscore.js或者lo-dash你便知道,这两个库中很多方法你可以传递一个参数来显示指定执行的上下文。比如_.each。自ECMAScript 5 标准后,一些原生的JS方法也允许传递上下文,比如forEach。事实上,上文提到的bind,apply还有call 已经给我们手动指定函数执行上下文的能力了。
    
    function Thing(type) {
        this.type = type;
    }
    Thing.prototype.log = function (thing) {
        console.log(this.type, thing);
    }
    Thing.prototype.logThings = function (arr) {
       arr.forEach(this.log, this); // logs "fruit apples..."
       _.each(arr, this.log, this); //logs "fruit apples..."
    }
    
    var thing = new Thing("fruit");
    thing.logThings(["apples", "oranges", "strawberries", "bananas"]);

    另一篇理解

  • 相关阅读:
    efwplus框架
    注册区域
    社招面试记录与总结
    验证码 Captcha 之大插件
    发生内存泄漏?
    Flume+LOG4J+Kafka
    协议如何保证可靠传输
    oracle之spool详细使用总结(转)
    SSH协议详解(转)
    oracle nologging用法(转)
  • 原文地址:https://www.cnblogs.com/xiaohuasan/p/5552713.html
Copyright © 2020-2023  润新知