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的模拟实现
相关推荐
NoneCoder31 分钟前
JavaScript系列(38)-- WebRTC技术详解
开发语言·javascript·webrtc
python算法(魔法师版)39 分钟前
html,css,js的粒子效果
javascript·css·html
小彭努力中1 小时前
16.在Vue3中使用Echarts实现词云图
前端·javascript·vue.js·echarts
flying robot1 小时前
React的响应式
前端·javascript·react.js
来一碗刘肉面1 小时前
Vue - ref( ) 和 reactive( ) 响应式数据的使用
前端·javascript·vue.js
guhy fighting2 小时前
原生toFixed的bug
前端·javascript·bug
约定Da于配置7 小时前
uniapp封装websocket
前端·javascript·vue.js·websocket·网络协议·学习·uni-app
村口蹲点的阿三9 小时前
Spark SQL 中对 Map 类型的操作函数
javascript·数据库·hive·sql·spark
noravinsc10 小时前
python md5加密
前端·javascript·python
微光无限12 小时前
Vue3 中使用组合式API和依赖注入实现自定义公共方法
前端·javascript·vue.js