我的前端坑
  • 关于本书
  • CSS JS 常用
    • 常用 CSS 样式
    • 坑爹的 CSS
    • sass 入门
    • canvas 总结
    • 常用 JS 函数
    • 表单和 FormData 对象
    • 水平滑动 tab 居中
    • js 中的 this
    • sse 和 fetch
    • js 原型链与 class 类
  • TypeScript
    • TS 概念
    • interface 与 type
    • interface 接口
    • Ts 配合 ESLint 和 prettier
  • 小程序
    • 常用小程序代码
  • VUE
    • VUE2 小技巧
    • VUE-CLI 中的 NODE_ENV
  • VUE3
    • VUE3 自行构建 sfc 遇到的坑
    • VUE3 v-model 的实现
    • VUE3 使用总结
    • VUE3 ref
  • vite
    • vite
  • http 请求
    • 前端实现下载
    • cors 跨域请求
    • windows hosts 文件
    • err_blocked_by_client 错误
  • 前端工具
    • npm 和 Node
      • 常见问题
      • npmTips
      • package.json 与 package-lock.json
      • npx
      • exports 与 module.exports
      • ESLint
    • VIM
      • vim 常用
    • Git
      • Git 常用命令
      • Git 小 tips
    • express
  • 后端工具
    • mysql 常见问题
    • docker 常见问题
    • docker
  • java
    • java 常用
    • lambda 表达式
    • java 字符串
    • java 泛型
    • java 反射
    • intellij IDEA
    • 多态
    • java 包管理
    • sql 查询语言
    • java 反射
    • java 异常
    • java 集合
    • spring
  • 命令行
    • 命令行 常用
  • 专利撰写 ppt
  • 后台简述
Powered by GitBook
On this page
  • this 的值
  • this 到底指向谁
  • ES6 中箭头函数与 this
  • 类中的 this

Was this helpful?

  1. CSS JS 常用

js 中的 this

this 的值

  1. 非严格模式下,this 总是指向一个对象

  2. 严格模式下,this 可以是任意值

  3. 无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象

this 到底指向谁

和 this 相关的函数的常用调用方式:

  1. 纯粹的函数调用

  2. 作为对象方法的调用

  3. call 调用 或 apply 调用

  4. 作为构造函数调用

来看一道题:

var x = 0;
var y = 1;
// 普通函数
function test() {
  console.log(this.x);
}
// 构造函数
function Test2() {
  this.y = 2;
}
var obj = {
  x: 1,
  test: test,
};
var obj2 = new Test2();

var arr = [test, new Test2()];

// ----- 以下各调用方式中的this值就是call()中的第一个参数 -----

// 纯粹的函数调用
test(); // 打印 0 该式可看作:test.call(undefined)

// 作为对象方法的调用
obj.test(); // 打印 1 该式可看作:obj.test.call(obj)

// call 调用 或 apply 调用
obj.test.call(); // 打印 0 该式可看作:obj.test.call(undefined)
obj.test.call(obj); // 打印 1

// 作为构造函数调用
console.log(obj2.y); // 打印 2 该式可看作:obj2.y.call(obj2)

// 在数组中
arr[0](); // 打印 undefined 该式可看作 arr.0.call(arr)
arr[1].y; // 值为 2 该式可看作 arr[1].y.call(arr[1])

以上我们最常见到的是类似 test(arg1,arg2, ...) obj.test(arg1,arg2, ...) 这样 的调用方式,而很少使用类似 obj.test.call(thisArg,arg1,arg2, ...) 这样的调用方 式。所以才会搞不清楚 this 到底指向了哪里。

test(arg1,arg2, ...) 等价于 test.call(undefined,arg1,arg2, ...) obj.test(arg1,arg2, ...) 等价于obj.test.call(obj,arg1,arg2, ...) 所以总结起来都是在以 func.call(thisArg,arg1,arg2, ...) 这样的的方式调用函数。

那么 this 所指向的值就很明了了:

** this 的值,就是 call(thisArg,arg1,arg2, ...) 里的 thisArg 的值。**

call 函数和 apply 函数用法类似,所以这里就不讨论 apply 了。

需要注意的是:call 函数中指定的 this 值并不一定是该函数执行时真正的 this 值,如 果这个函数处于 non-strict mode 模式,则指定为 null 和 undefined 的 this 值会自动 指向全局对象(浏览器中就是 window 对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象(严格模式下默认 thisArg 是 undefined)。

ES6 中箭头函数与 this

先看个示例(不建议在定义对象的方法时使用箭头函数,这里只是为了探讨问题而使用):

var obj = {
  name: "fpc",
  tool: () => {
    console.log(this);
  },
  tool2: function () {
    console.log(this);
  },
};
obj.tool(); // 打印 window 对象 相当于 obj.tool.call(obj)
obj.tool2(); // 打印 obj 对象 相当于 obj.tool2.call(obj)
obj.tool.call(); // 打印 window 对象  obj.tool.call(undefined)
obj.tool2.call(); // 打印 window 对象  obj.tool2.call(undefined)
obj.tool.call(obj); // 打印 window 对象
obj.tool2.call(obj); // 打印 obj 对象

tool 方法用箭头函数定义,tool2 方法用普通函数定义。

从 obj.tool.call(obj) // 打印 window 对象 可以看出,即使用 call 指定了 this 值 为 obj,打印出来的依然是 window 对象。而 obj.tool2.call(obj) // 打印 obj 对象 就正常输出 call 指定的 obj 对象。

阮一峰老师的 ES6 教程里是这样解释的: 箭头函数内的 this 对象,就是定义时所在的 对象,而不是使用时所在的对象。

回到这个例子中,就是说 箭头函数 tool 定义时所在的作用域为全局作用域,所以它里面 的 this 指向 window 。这里会有很多人不能理解,tool 不是 obj 中定义的方法吗?怎么 会指向 window 。我最开始也一脸迷茫,想了好久才明白:

this 值的判断条件并 不是对象,而是 所在作用域 ,js 只有 函数作用域 和 全局作用域 (这里先说 ES5,ES6 暂不讨论)所以可以这样理解: 箭头函数中 必须通过查找作用域来确定其 this 值,简单来说就是:箭头函数中 this 绑定的是距它最 近一层非箭头函数作用域的 this 值。

箭头函数 tool 往外一层就是全局作用域了,所以 tool 中的 this 就是 window 。

这样理解还是有点复杂,再简化一下:箭头函数里没有 this ,如果里面出现 this ,就 向上查找最近的 this 值来使用。

再来看一个例子:

var obj = {
  props: 37,
  foo: () => {
    return this.props;
  },
  foo2: function () {
    return this.props;
  },
  bar: function () {
    var x = () => this;
    return x;
  },
  bar2: function () {
    "use strict";
    var x = () => this;
    return x;
  },
};

// obj.foo()≈obj.foo.call(obj) 因为obj.foo是箭头函数,所以 this 是它往上一层非箭头函数作用域,
// 即全局作用域 window
//  window 没有 props 属性,所以返回 undefined
console.log(obj.foo()); // undefined

// obj.foo2()≈obj.foo2.call(obj) this 则为 obj obj.props 是37
console.log(obj.foo2()); // 37

var fn = obj.bar();
// 距离箭头函数 x 最近的非箭头函数作用域是 obj.bar()
// 再根据 obj.bar()≈obj.bar.call(obj) 得出 x 中的 this 是 obj
console.log(fn() === obj); // true

var fn2 = obj.bar;
// 距离箭头函数 x 最近的非箭头函数作用域是 fn2()
// 再根据 fn2()≈fn2.call(undefined) 得出 x 中的 this 是 window (非严格模式下)
console.log(fn2()() == window); // true

var fn2 = obj.bar2;
// 距离箭头函数 x 最近的非箭头函数作用域是 fn2()
// 再根据 fn2()≈fn2.call(undefined) 得出 x 中的 this 是 undefined (严格模式下)
console.log(fn2()() == undefined); // true

再看一个例子:

var o = { prop: 37 };

function independent() {
  return this.prop;
}

let independent2 = () => {
  return this.prop;
};
o.f = independent;
o.f2 = independent2;

console.log(o.f()); // 37
console.log(o.f2()); // undefined

类中的 this

和其他普通函数一样,类的实例的方法中的 this 值取决于它们如何被调用,有时,改写这 个行为,让类中的 this 值总是指向这个类实例会很有用。为了做到这一点,可在构造函数 中绑定类方法。

仔细看下题:

class Car {
  constructor() {
    this.type = "实例的车";
    this.boot = this.boot.bind(this);
  }
  boot() {
    console.log("原型方法:启动!type是:" + this.type);
  }
  boot2() {
    console.log("原型方法:启动!type是:" + this.type);
  }
  boot3 = () => {
    console.log("原型方法:启动!type是:" + this.type);
  };
  static trademark() {
    console.log("每辆车都有自己的品牌");
  }
}

let car = new Car();
let boot = car.boot;
let boot2 = car.boot2;
let boot3 = car.boot3;

// boot 绑定 this 到实例上,所以两种调用方式 this 都指向实例对象
car.boot(); // "原型方法:启动!type是:实例的车"
boot(); //  "原型方法:启动!type是:实例的车"

// boot2 未绑定 this 到实例上,
// 第一个的 this 指向实例对象
// 第二个的 this 指向undefined,又因为 class 是严格模式下执行的,所以undefined不会转向 window 从而报错
car.boot2(); //  "原型方法:启动!type是:实例的车"
boot2(); //  "TypeError: Cannot read properties of undefined (reading 'type')"

// boot3用箭头函数定义
car.boot3(); // "原型方法:启动!type是:实例的车"
boot3(); //  "原型方法:启动!type是:实例的车"
Previous水平滑动 tab 居中Nextsse 和 fetch

Last updated 2 years ago

Was this helpful?