image.png
image.png
image.png

this都有一个共同点:它总是返回一个对象

this就是属性或方法“当前”所在的对象

this就是function.call()的第一个参数

  1. function f() {
  2. return '姓名:'+ this.name;
  3. }
  4. var A = {
  5. name: '张三',
  6. describe: f
  7. };
  8. var B = {
  9. name: '李四',
  10. describe: f
  11. };
  12. A.describe() // "姓名:张三"
  13. B.describe() // "姓名:李四"
  14. //this始终指向当前
  15. var name = 'kangkang'
  16. var f = function() {
  17. return this.name;
  18. }
  19. console.log(f()) //kangkang
  20. var name = 'kangkang'
  21. var f = function() {
  22. return name;
  23. }
  24. console.log(f()) //kangkang

实质

  1. var f = function () {
  2. console.log(this.x);
  3. }
  4. var x = 1;
  5. var obj = {
  6. f: f,
  7. x: 2,
  8. };
  9. // 单独执行
  10. f() // undefined
  11. // obj 环境执行
  12. obj.f() // 2

函数预编译过程 this 指向 window,new 函数() this就指向对象

image.png

谁调用方法,this就指向谁
image.png

使用注意点

函数里this的指向

未被对象调用的函数里的this就指向全局,new以后就变成对象

  1. var name = '222'
  2. var a = {
  3. name: '111',
  4. say: function() {
  5. console.log(this.name)
  6. },
  7. }
  8. var fun = a.say //函数的声明给了fun
  9. fun() //222 fun里的this指向全局
  10. a.say() //111
  11. var b = {
  12. name: '333',
  13. say: function(fun) {
  14. fun() //没有谁在调用fun,所以fun里的this指向全局
  15. },
  16. }
  17. b.say(a.say) //222
  18. b.say = a.say
  19. b.say() //333

image.png

  1. var a = 5
  2. function test() {
  3. a = 0 //testAO里的a
  4. console.log(a) //0
  5. console.log(this.a) //this指向全局 5
  6. var a //定义提升 把a放到了testAO里
  7. console.log(a) //0
  8. }
  9. test()
  10. new test() //把this变成了{}

避免多层 this

由于this的指向是不确定的,所以切勿在函数中包含多层的this

  1. //嵌套函数指向全局对象
  2. var a = 2
  3. var o = {
  4. a: 1,
  5. f1: function () {
  6. console.log(this.a);
  7. var f2 = function () {
  8. console.log(a);
  9. }();
  10. }
  11. }
  12. console.log(o.f1())
  13. //1
  14. //2
  15. var a = 2
  16. var o = {
  17. a: 1,
  18. f1: function () {
  19. console.log(this.a);
  20. var that = this
  21. var f2 = function () {
  22. console.log(that.a);
  23. }();
  24. }
  25. }
  26. console.log(o.f1())
  27. //1
  28. //1

避免数组处理方法中的 this

数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this

  1. var o = {
  2. v: 'hello',
  3. p: [ 'a1', 'a2' ],
  4. f: function f() {
  5. this.p.forEach(function (item) {
  6. console.log(this.v + ' ' + item);
  7. });
  8. }
  9. }
  10. o.f()
  11. // undefined a1
  12. // undefined a2
  13. //将this当作foreach方法的第二个参数,固定它的运行环境
  14. var o = {
  15. v: 'hello',
  16. p: [ 'a1', 'a2' ],
  17. f: function f() {
  18. this.p.forEach(function (item) {
  19. console.log(this.v + ' ' + item);
  20. }, this);
  21. }
  22. }
  23. o.f()
  24. // hello a1
  25. // hello a2

避免回调函数中的 this

  1. var o = new Object();
  2. o.f = function () {
  3. console.log(this === o);
  4. }
  5. // jQuery 的写法
  6. $('#button').on('click', o.f);
  7. //点击按钮以后,控制台会显示false。原因是此时this不再指向o对象,而是指向按钮的 DOM 对象

绑定 this 的方法

JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向

Function.prototype.call()

所有函数的调用,其实都是 func———>func.call()

image.png
借用其他函数,实现自己功能
image.png

  1. function Wheel(style, wheelSize) {
  2. this.style = style
  3. this.wheelSize = wheelSize
  4. }
  5. function Sit(c, siteColor) {
  6. this.c = c
  7. this.siteColor = siteColor
  8. }
  9. function Car(name, style, wheelSize, c, siteColor) {
  10. this.name = name
  11. Wheel.call(this, style, wheelSize)
  12. Sit.call(this, c, siteColor)
  13. }
  14. var car = new Car('BMW', '花里胡哨', 100, '真皮', '红色')
  15. console.log(car)
  16. //Car {name: 'BMW', style: '花里胡哨', wheelSize: 100, c: '真皮', siteColor: '红色'}

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数

  1. var obj = {};
  2. var f = function () {
  3. return this;
  4. };
  5. console.log(f.call(obj) === obj) // true

call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象
call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数

  1. function add(a, b) {
  2. return a + b;
  3. }
  4. add.call(this, 1, 2) // 3

call方法的一个应用是调用对象的原生方法

  1. var obj = {};
  2. obj.hasOwnProperty('toString') // false
  3. // 覆盖掉继承的 hasOwnProperty 方法
  4. obj.hasOwnProperty = function () {
  5. return true;
  6. };
  7. obj.hasOwnProperty('toString') // true
  8. Object.prototype.hasOwnProperty.call(obj, 'toString') // false

Function.prototype.apply()

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加

  1. function f(x, y){
  2. console.log(x + y);
  3. }
  4. f.call(null, 1, 1) // 2
  5. f.apply(null, [1, 1]) // 2

找出数组最大元素
var a = [10, 2, 4, 15, 9]; Math.max.apply(null, a) // 15

将数组的空元素变为undefined
Array.apply(null, ['a', ,'b']) // [ 'a', undefined, 'b' ]

转换类似数组的对象

  1. Array.prototype.slice.apply({0: 1, length: 1}) // [1]
  2. Array.prototype.slice.apply({0: 1}) // []
  3. Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
  4. Array.prototype.slice.apply({length: 1}) // [undefined]

绑定回调函数的对象

  1. var o = new Object();
  2. o.f = function () {
  3. console.log(this === o);
  4. }
  5. var f = function (){
  6. o.f.apply(o);
  7. // 或者 o.f.call(o);
  8. };
  9. // jQuery 的写法
  10. $('#button').on('click', f);

Function.prototype.bind()

bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数

  1. var counter = {
  2. count: 0,
  3. inc: function () {
  4. this.count++;
  5. }
  6. };
  7. var obj = {
  8. count: 100
  9. };
  10. var func = counter.inc.call(obj);
  11. func();
  12. console.log(obj.count)
  13. //bind还可以接受更多的参数,将这些参数绑定原函数的参数
  14. var add = function (x, y) {
  15. return x * this.m + y * this.n;
  16. }
  17. var obj = {
  18. m: 2,
  19. n: 2
  20. };
  21. var newAdd = add.bind(obj, 5);
  22. newAdd(5) // 20

每一次返回一个新函数

  1. var listener = o.m.bind(o);
  2. element.addEventListener('click', listener);
  3. // ...
  4. element.removeEventListener('click', listener);

结合回调函数使用

  1. var counter = {
  2. count: 0,
  3. inc: function () {
  4. 'use strict';
  5. this.count++;
  6. }
  7. };
  8. function callIt(callback) {
  9. callback();
  10. }
  11. callIt(counter.inc.bind(counter));
  12. counter.count // 1

上面代码中,callIt方法会调用回调函数。这时如果直接把counter.inc传入,调用时counter.inc内部的this就会指向全局对象。使用bind方法将counter.inc绑定counter以后,就不会有这个问题,this总是指向counter

某些数组方法可以接受一个函数当作参数。这些函数内部的this指向,很可能也会出错

  1. var obj = {
  2. name: '张三',
  3. times: [1, 2, 3],
  4. print: function () {
  5. this.times.forEach(function (n) {
  6. console.log(this.name);
  7. });
  8. }
  9. };
  10. obj.print()
  11. // 没有任何输出
  12. obj.print = function () {
  13. this.times.forEach(function (n) {
  14. console.log(this.name);
  15. }.bind(this));
  16. };

结合call方法使用

  1. var push = Function.prototype.call.bind(Array.prototype.push);
  2. var pop = Function.prototype.call.bind(Array.prototype.pop);
  3. var a = [1 ,2 ,3];
  4. push(a, 4)
  5. a // [1, 2, 3, 4]
  6. pop(a)
  7. a // [1, 2, 3]

callee caller

arguments.callee 返回当前函数

  1. var num = (function(n) {
  2. if (n == 1) {
  3. return 1
  4. }
  5. return n * arguments.callee(n - 1)
  6. })(100)
  7. console.log(num)

caller 返回调用的环境

  1. function test() {
  2. demo()
  3. }
  4. test() //f test
  5. function demo() {
  6. console.log(demo.caller)
  7. }

链式调用

image.png