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...

相关推荐
霍先生的虚拟宇宙网络6 分钟前
webp 网页如何录屏?
开发语言·前端·javascript
温吞-ing8 分钟前
第十章JavaScript的应用
开发语言·javascript·ecmascript
彪8259 分钟前
第十章 JavaScript的应用 习题
javascript·css·ecmascript·html5
Myli_ing2 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风2 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave2 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟2 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
疯狂的沙粒3 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript