JavaScript 中的this

this

this 是函数运行时函数体内部产生的一个对象,只能在函数体内部访问,this 的用法有这么几种

js 复制代码
function fn() {
  this.x = 1;
}
  1. 在纯粹的函数调用中,this 属于全局调用,this 就是全局对象
js 复制代码
var x = 1;
function fn() {
  console.log(this.x);
}
fn(); // 1
  1. 作为对象方法调用,函数可以在对象中调用,此时 this 就指向该对象
js 复制代码
function fn() {
  console.log(this.x);
}
var obj = {};
obj.x = 'xxx';
obj.fn = fn;
obj.fn(); // xxx
  1. 作为构造函数调用,在 js 中任何一个函数都可以作为构造函数,通过 new 产生一个新对象,这时这个 this 就指向这个新对象
js 复制代码
function fn() {
  this.x = 1;
}
var obj = new fn();
obj.x; // 1

深入了解一下 this

先看一段代码

js 复制代码
var obj = {
  fn:function(){
    console.log(this.test)
  }
  test:'test'
}
var fn = obj.fn
var test = '另一个test'

obj.fn() // test
fn() // 另一个test

这在 js 中是一个再正常不过的现象,这是因为在函数体内部使用了 this 关键字,说到底 this 指的是函数运行时所在的环境,那么为什么会这样呢,这就涉及到 js 中 this 的设计原理了,这其实跟内存的数据结构有关系

js 复制代码
var obj = {
  name: '666',
};

上述代码会将一个对象赋值给变量 obj ,我们知道在 js 中非基本数据类型是存在中的,而基本数据类型是存在栈中的,在 js 引擎执行到上述代码时,js 引擎会在内存生成一个对象 { name: 666 },然后把这个对象的内存地址赋值给变量 obj,就是说 obj 只是一个内存地址,这个地址指向的是在堆中存放的 { name: 666} 这个对象的地址。在读取 obj.name 时,js 引擎会先去访问变量 obj 从 obj 中拿到内存地址,然后再从改地址中得到 name 属性,并返回该属性。 在 js 中原始对象并不是行我们写的 { name: 666 } 这样,在内存中其实是以一种字典的形式存储的,每一个属性都会对应一个属性描述符,(Vue2 的响应式原理就是基于属性描述符实现的),name 属性的值保存在属性描述符的 value 属性中

这样的数据结构是合理清晰的,但是有一种特殊情况,属性的值可以是一个函数,这就不一样了,再看一段代码

js 复制代码
var obj = {
  fn: function () {},
};

在 js 引擎中会将函数单独保存在内存中,再将函数的地址赋值给 obj 的 fn 属性,这就不一样了,由于函数是一个单独的值,所以就可以在不同的上下文环境中执行,来看一张图

运行环境的影响

在 js 中允许在函数体内部,引用当前环境的其他变量,那么问题就来了,函数可以在不同的环境中运行,所以需要有一个东西或者说某一种机制去获取当前函数内部的运行环境(上下文),this 就很好的解决了这个问题,this 就是在函数体内部,指代当前函数的运行环境。来看一段代码和一张图

js 复制代码
function fn() {
  console.log(this.flag);
}

var flag = 'flag';
var obj = {
  fn: fn,
  flag: 'flag in obj',
};

// 单独执行
fn(); // flag

// 在 obj 上下文中执行
obj.fn(); // flag in obj

call apply bind

call apply bind 都是用来改变 this 指向 在函数调用时动态改变函数上下文,但是他们直接也有不同,可以分为两类

  1. call apply 这两个立即调用,对于这两个来说,两个 api 的作用完全一样只不过是传参的方式不同
  2. bind 是返回一个函数,便于后期调用

call

js 复制代码
/**
 * ctx 上下文
 * 剩余参数为传递给函数的参数
*/
  function.call(ctx,arg1,arg2...)
js 复制代码
// 实现一个简单的call
Function.prototype.myCall = function (ctx, ...args) {
  // ctx 为空默认为全局 window
  ctx = ctx || window;
  // 创建一个唯一标识符
  const fnSymbol = Symbol();
  // 将原始函数保存为 ctx 对象的一个属性,this 就是原始函数
  ctx[fnSymbol] = this;
  // 调用函数并将结果存储在 result 中
  const result = ctx[fnSymbol](...args);
  // 删除 ctx 对象属性
  delete ctx[fnSymbol];
  // 返回结果
  return result;
};

apply

apply 与 call 类似 但是 apply 函数需要将参数作为数组传递

js 复制代码
  /**
   * ctx 上下文
   * 剩余参数
  */
  function.apply(ctx,[argsArray])
js 复制代码
// 实现一个简单的 apply 其实跟 call 一样
Function.prototype.myApply = function (ctx, args = []) {
  // 默认指向 window
  ctx = ctx || window;
  // 创建一个唯一标识
  const fnSymbol = Symbol();
  // 存储原始函数
  ctx[fnSymbol] = this;
  // 调用函数存储结果
  const result = ctx[fnSymbol](...args);
  // 删除属性
  delete ctx[fnSymbol];
  // 返回结果
  return result;
};

bind

bind 与 call apply 不同 bind 不会立即调用 而是会返回一个新函数,该函数将绑定到指定的上下文,当函数被调用时候在指定的上下文运行

js 复制代码
/**
 * ctx 上下文
 * 剩余参数为传递给函数的参数
*/
function.bind(ctx,arg1,arg2,....)
js 复制代码
// 实现一个简单的 bind
Function.prototype.myBind = function (ctx, ...args) {
  // 存储 this
  const fn = this;
  // 返回一个新的函数,该函数将传入的参数与新函数的参数合并,并在新的上下文中使用 apply 调用原始函数
  return function (...newArgs) {
    return fn.apply(ctx, [...args, ...newArgs]);
  };
};

参考连接

www.ruanyifeng.com/blog/2018/0...

juejin.cn/post/720758...

相关推荐
万物得其道者成12 分钟前
React Zustand状态管理库的使用
开发语言·javascript·ecmascript
小白小白从不日白12 分钟前
react hooks--useReducer
前端·javascript·react.js
下雪天的夏风25 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom36 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
volodyan40 分钟前
electron react离线使用monaco-editor
javascript·react.js·electron
^^为欢几何^^1 小时前
lodash中_.difference如何过滤数组
javascript·数据结构·算法
Hello-Mr.Wang1 小时前
vue3中开发引导页的方法
开发语言·前端·javascript
程序员凡尘1 小时前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
北岛寒沫6 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
everyStudy6 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript