关于this指向

306 阅读3分钟
// 一个函数是直接调用的,this指向window。
//哪个对象调用了函数,函数里面的this就指向谁
// 实际上this的最终指向的是那个调用它的对象
//但是在箭头函数里面this指向的是定义时的对象而不是调用时的对象

// 如果一个函数中有this,
// 但是它没有被上一级的对象所调用,
// 那么this指向的就是window,
// 严格版中的默认的this不再是window,而是undefined

// this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 
// 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 
// 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。
// prototype本质上还是一个JavaScript对象。 并且每个函数都有一个默认的prototype属性。

function 模拟实现call() {
    将函数设为对象的属性
    执行该函数
    删除该函数
    var foo = {
        value: 1
    };
    function bar() {
        console.log(this.value);
    }
    // 第一步
    foo.fn = bar
    // 第二步
    foo.fn()
    // 第三步
    delete foo.fn
    // 对于后面的参数:
    // 我们可以从 Arguments 对象中取值,取出第二个到最后一个参数,然后放到一个数组里。
    // 以上个例子为例,此时的arguments为:
    // arguments = {
    //      0: foo,
    //      1: 'kevin',
    //      2: 18,
    //      length: 3
    // }
    // 因为arguments是类数组对象,所以可以用for循环
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    // 执行后 args为 [foo, 'kevin', 18]

    // 不定长的参数问题解决了,我们接着要把这个参数数组放到要执行的函数的参数里面去。
    // 我们这次用 eval 方法拼成一个函数,类似于这样:
    eval('context.fn(' + args +')')
    // 这里 args 会自动调用 Array.toString() 这个方法。
    Function.prototype.call2 = function(context) {
        context.fn = this;
        var args = [];
        for(var i = 1, len = arguments.length; i < len; i++) {
            args.push('arguments[' + i + ']');
        }
        eval('context.fn(' + args +')');
        delete context.fn;
    }

    // this 参数可以传 null,当为 null 的时候,视为指向 window
    Function.prototype.call2 = function (context) {
        var context = context || window;
        context.fn = this;
        var args = [];
        for(var i = 1, len = arguments.length; i < len; i++) {
            args.push('arguments[' + i + ']');
        }
        var result = eval('context.fn(' + args +')');
        delete context.fn
        return result;
    }      
    



}
function 模拟实现apply() {
    Function.prototype.apply = function (context, arr) {
        var context = Object(context) || window;
        context.fn = this;
        var result;
        if (!arr) {
            result = context.fn();
        }
        else {
            var args = [];
            for (var i = 0, len = arr.length; i < len; i++) {
                args.push('arr[' + i + ']');
            }
            result = eval('context.fn(' + args + ')')
        }
     
        delete context.fn
        return result;
    }         

}
function 模拟实现bind() {
    // bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数
    Function.prototype.bind2 = function (context) {
        if (typeof this !== "function") {
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
        }
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
        var fNOP = function () {};
        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
        }
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    }
    
}
----------------------------------
1.
function a(){
    var user = "追梦子";
    console.log(this.user); //undefined
    console.log(this); //Window
}
a();
2.
var o = {
    user:"追梦子",
    fn:function(){
        console.log(this.user);  //追梦子
    }
}
o.fn();
3.
var o = {
    user:"追梦子",
    fn:function(){
        console.log(this.user); //追梦子
    }
}
window.o.fn();
// 这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
4.
var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();
// this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,
// 例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,
// 这和例子3是不一样的,例子3是直接执行了fn。

5.
function fn(){  
    this.user = '追梦子';  
    return {};  
}
var a = new fn;  
console.log(a.user); //undefined

function fn(){  
    this.user = '追梦子';  
    return undefined;
}
var a = new fn;  
console.log(a.user); //追梦子
// 如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
// 还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。
function fn(){  
    this.user = '追梦子';  
    return null;
}
var a = new fn;  
console.log(a.user); //追梦子   
----------------------------------
function b() {
    return function () {
        return this;
    }
}
console.log(b()()); //This:指向windows

function c() {
    return {
        s: function () {
            return this;
        }
    }
}
console.log(c().s()); //This:指向s