JavaScript你也可以掌控的this指向

this 驰骋沙场

1.环境。
在 window 环境下,全局 this 指向 window:

Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window,}
}

可以通过 this 访问 window 下的属性。

在 node 环境下,this 指向 global。

Object [global] {
  DTRACE_NET_SERVER_CONNECTION: [Function],
  DTRACE_NET_STREAM_END: [Function],
  DTRACE_HTTP_SERVER_REQUEST: [Function],
  DTRACE_HTTP_SERVER_RESPONSE: [Function],
  DTRACE_HTTP_CLIENT_REQUEST: [Function],
  DTRACE_HTTP_CLIENT_RESPONSE: [Function],
  global: [Circular],
  process:
   process {
...

2.调用。
普通用法 this 指向调用它的实例。
1)全局函数

function test() {
  console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
}
test();

2)定时器
window 环境定时器中的 this 指向 window。

setTimeout(function() {
  console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
});

node 环境的 this 指向 Timeout.

3)字面量中的 this

var name = "bbb";
var obj = {
  name: "aaa",
  mes: function() {
    console.log(this.name); // aaa
  }
};

obj.mes();

function MES() {
  console.log(this.name); // window: bbb, node: undefined
}

MES();

这个 this 在 mes 方法中,但是调用 mes 方法的是 obj,所以 this 指向 obj。MES 方法被 window 调用,所以指向全局。

再看一个例子:

var obj = {
  name: "aaa",
  obj: {
    c: "ccc"
  },
  test: this.name,
  text: function() {
    return "text:" + this.name;
  },
  mes: function() {
    console.log(this.obj.c);
  }
};

obj.mes(); // ccc
console.log(obj.test); // undefined
console.log(obj.text()); // url:aaa

这里的操作是想在 test 属性中访问 name,但是这里的 this 是指向 window,所以会查找 window 中有没有 name 属性,如果有就是 window 中的值,没有就是 undefined。这时候可以使用 obj 调用 text()方法一个,text 方法中返回值作为值,此时的 this 指向 obj。或者 test: this.obj.name。

4)构造函数
指向构造函数的实例

function Test(name, age) {
  this.name = name;
  this.age = age;
}

var test = new Test("ooo");
console.log(test.name); // ooo

5)事件绑定
指向绑定事件的 DOM

var dom = document.getElementById("div-id");
dom.onclick = function() {
  console.log(this); // DOM
};

6)原型

function Test(name, age) {
  this.name = name;
  this.age = age;
}

Test.prototype.mes = function() {
  console.log(this.name); // 指向构造函数 Test { name: 'ooo', age: undefined }
};

var test = new Test("ooo");
test.mes();

9)改变指向
使用 call、apply、bind

var obj = {
  name: "aaa",
  age: "bbb"
};

function mes(info) {
  console.log(this.name + info);
}

console.log(mes()); // window/golbal

mes.call(obj, "呵呵"); // 'aaa呵呵'
// mes.apply(obj, ["呵呵"]);
// mes.bind(obj, "呵呵")();

在这里 call 和 appl 的使用方法相同,不同的是 call、apply 传的参数。call(this,arg,arg),
apply(this,arguments)。call 第二个参数是单个添加的序列,apply 第二个参数为参数数组。
bind 和 call、apply 的区别是,call/apply 直接作用在函数上并执行函数,而 bind 作用在 mes 函数上返回一个函数,所以不会立即执行。

bind 方法的实现:

Function.prototype.bind = function(context) {
  var that = this;
  var args = Array.prototype.slice.call(arguments, 1);

  var bound = function() {
    if (this instanceof bound) {
      // 如果是作为构造函数,那么第一个参数 context 就不需要了
      return that.apply(
        this,
        args.concat(Array.prototype.slice.call(arguments))
      );
    } else {
      // 拼接参数
      return that.apply(
        context,
        args.concat(Array.prototype.slice.call(arguments))
      );
    }
  };
  // 维护原型关系
  if (this.prototype) {
    bound.prototype = this.prototype;
  }

  return bound;
};

这里频繁使用了Array.prototype.slice.call(),slice 是 Array 类里的一个方法,Array.prototype.slice表示截取数组的一部分,slice(start,end): 返回 start 到 end (不包括该元素)的 arrayObject 中的元素,如果没有 end 参数,则从选 start 至末尾的元素。call(arguments,1)表示从第二个参数开始。

function test(a, b, c, d) {
  var arg = Array.prototype.slice.call(arguments, 1); // 调用slice(1)
  alert(arg);
}
test("a", "b", "c", "d"); // [ 'b', 'c', 'd' ]

Array.prototype.slice.call()可以理解为:改变数组的 slice 方法的作用域(因为 arguments 并不是真正的数组对象,只是与数组类似,它并没有 slice 这个方法),在特定作用域中去调用 slice 方法,call()方法的第二个参数表示传递给 slice 的参数即截取数组的起始位置。
原理: Array.prototype.slice.call(arguments)能将具有 length 属性的对象(key 值为数字)转成数组。[]是 Array 的示例,所以可以直接使用[].slice()方法。

var obj = { 0: "hello", 1: "world", length: 2 };
console.log(Array.prototype.slice.call(obj, 0)); //["hello", "world"]   调用slice(0)

没有 length 属性的对象

var obj = { 0: "hello", 1: "world" }; //没有length属性
console.log(Array.prototype.slice.call(obj, 0)); //[]

10)es6
默认指向在定义它时,它所处的对象,而不是执行时的对象, 定义它的时候,可能环境是 window(即继承父级的 this)

var obj = {
  say: function() {
    setTimeout(() => {
      console.log(this);
    });
  }
};
obj.say(); // obj
var obj = {
  say: function() {
    var f1 = () => {
      console.log(this); // obj
      setTimeout(() => {
        console.log(this); // obj
      });
    };
    f1();
  }
};
obj.say();

f1 继承父级 this 指代的 obj,不管 f1 有多层箭头函数嵌套,都是 obj.

var obj = {
  say: function() {
    var f1 = function() {
      console.log(this); // window, f1调用时,没有宿主对象,默认是window
      setTimeout(() => {
        console.log(this); // window,继承父级的this,父级的this指代的是window.
      });
    };
    f1();
  }
};
obj.say();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值