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的模拟实现
相关推荐
布兰妮甜3 分钟前
单例模式在前端(JavaScript)中的实现与应用
前端·javascript·单例模式
Mintopia4 分钟前
Three.js 加载模型文件:从二进制到像素的奇幻漂流
前端·javascript·three.js
StrongerIrene13 分钟前
rs build 的process.env的值undefined解决方案
开发语言·javascript·ecmascript
前端小巷子22 分钟前
跨域问题解决方案:JSONP
前端·javascript·面试
just小千1 小时前
重学React(二):添加交互
javascript·react.js·交互
qq_589568101 小时前
element-plus按需自动导入的配置 以及icon图标不显示的问题解决
开发语言·javascript·ecmascript
菌菇汤2 小时前
uni-app实现单选,多选也能搜索,勾选,选择,回显
前端·javascript·vue.js·微信小程序·uni-app·app
Ramos丶2 小时前
【ABAP】 从无到有 新建一个Webdynpro程序
java·前端·javascript
摸鱼仙人~2 小时前
如何创建基于 TypeScript 的 React 项目
javascript·react.js·typescript
qq_411671982 小时前
vue3 的模板引用ref和$parent
前端·javascript·vue.js