this指针与Call/Apply/Bind,new操作符

this指针与Call/Apply/Bind,new操作符

this 指针的指向

  1. 对象属性赋值方法,方法内 this 指向对象本身,
  2. 如果是变量赋值方法,方法内 this 指向 window
  3. (上述可以认为是一个全局定义的函数赋值给变量,map、foreach 内部function 也可以认为是全局定义的function,故也是 window).
  4. 如果将对象属性定义的方法赋值给变量,方法内 this 指向 window.
  5. 变量找不到会去外层找,但是this不会,this找不到就是window.

Call

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

举个栗子

csharp 复制代码
function foo () { 
	console.log(this, this.value) 
}
let obj = { value: 2 };
foo.call(obj); // obj, 2

执行步骤可以理解为

  1. 将函数设置为转换的this对象属性(因为对象中运行函数,this指向该对象)
  2. 执行该对象中的函数
  3. 删除对象中该属性
js 复制代码
Function.prototype.myCall = function(ctx, ...args) {
	ctx._callFn = this;
	ctx._callFn(...args);
	delete ctx._callFn;
}
function foo (...args) { 
	console.log(this, this.value, ...args) 
}
let obj = { value: 2 };
foo.myCall(obj, 1, 2, 3); // 2, 1, 2, 3

功能基本完成了,但有些小瑕疵

  1. this 可以为null, 为null时指向window
  2. 既然这个属性时没用的, 如果原目标有_callFn这个属性,就误删了, 用symbol()代替
  3. 函数是可以有返回值的
js 复制代码
Function.prototype.myCall = function (ctx , ...args) {
  const fnCtx = ctx || window;
  const fn = Symbol();
  fnCtx[fn] = this;
  const res = fnCtx.fn(...args);
  delete fnCtx[fn];
  return res;
};
function foo (...args) { 
  return this.value;
}
window.value = 3
let obj = { value: 2 };
console.log(foo.myCall(obj)); // 2
console.log(foo.myCall()); // 3

Apply

apply和Call灰常相似,只是把多个参数,改成参数数组, 只要把...args改成arg即可

js 复制代码
Function.prototype.myApply = function (ctx , argArr) {
  const fnCtx = ctx || window;
  const fn = Symbol();
  fnCtx[fn] = this;
  const res = fnCtx[fn](...argArr);
  delete fnCtx[fn];
  return res;
};
function foo (...args) { 
  console.log(...args);
  return this.value;
}
let obj = { value: 2 };
console.log(foo.myApply(obj, [1, 2])); // 1, 2     2

Bind

MDN 中这样介绍bind:

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

bind 其实跟call 很像,唯一区别就是bind返回的是函数, 而call返回的是调用函数后的值

举个栗子

js 复制代码
let obj = { value: 2 };
function foo(...args) { console.log(this.value, ...args) }
foo.bind(obj, 666)(); // 2 666
foo.call(obj, 666); // 2 666

因此,我们可以再call的基础上实现bind

js 复制代码
let obj = { value: 2 };
function foo(...args) { console.log(this.value, ...args) }
Function.prototype.myBind = function(ctx, ...args){
  const preArgs = args;
  return (...args) => this.call(ctx, ...preArgs, ...args); // 箭头函数的this指向外层
}
const bindFn = foo.myBind(obj, 6, 7)
bindFn(8, 9);  // 2, 6, 7, 8 , 9

new操作符

new 运算符允许开发人员创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例

辣么问题来了,如何手写一个new操作符

答案是不行的,因为new操作符是保留字,所以只能用function来模拟一个

new操作符实际进行的工作

  1. 创造一个新对象,新对象的原型指为构造函数的prototype
  2. 执行构造函数内的操作,获取构造函数的返回值
  3. 构造函数有返回值则返回返回值,没有则返回构造对象
js 复制代码
// 自定义new函数
function myNew(fn, ...args) {
  let obj = Object.create(fn.prototype);
  const result = fn.apply(obj, args);
  return result && typeof result === 'object' ? result : obj;  
}

// 测试代码
function Otaku (name, age) {
    this.name = name;
    this.age = age;
    this.habit = 'Games';
}
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function () {
    console.log('I am ' + this.name);
};

var person = myNew(Otaku, 'Kevin', '18');
console.log(person.name); // Kevin
console.log(person.habit); // Games
console.log(person.strength); // 60
person.sayYourName(); // I am Kevin

参考

  1. MDN
  2. JavaScript深入之call和apply的模拟实现
相关推荐
用户938515635079 小时前
从零构建《天龙八部》知识库:EPUB 加载→文本分割→向量嵌入→Milvus 存储→RAG 问答,一条链路打通
javascript·人工智能·全栈
柚yuzumi11 小时前
变量明明定义了,为什么访问不到?深入理解 JavaScript 作用域
javascript
何时梦醒11 小时前
React + TypeScript + Vite 实战:从零构建 Color Picker 应用
前端·javascript·架构
Highcharts.js11 小时前
基于Highcharts开发的OHLC 股票K线图完全指南
javascript·信息可视化·echarts·数据可视化·highcharts·ohlc
默_笙11 小时前
😋 我让 DeepSeek-R1 在浏览器本地跑了起来,后端同事说"你认真的?"
前端·javascript
JasonMa15313 小时前
从零搭建一个微信小程序活动管理平台(一):架构设计与技术选型
javascript·微信小程序·node.js
sugar__salt14 小时前
Vue3 核心特性深度解析:v-model、ref 属性与 nextTick 完全指南
前端·javascript·vue.js·前端框架·vue
古韵14 小时前
你的 uni-app 分页,一个 hook 搞定竞态和加载
javascript·vue.js·uni-app
成都渲染101云渲染666615 小时前
Blender渲染时,纯CPU渲染的设置教程
前端·javascript·blender
多加点辣也没关系16 小时前
JavaScript|第30章:事件机制
开发语言·javascript