这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。
平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态、习惯、思路清晰程度等。
注意是简单实现,不是完整实现,重要的是概念清晰和实现思路清晰,建议
先解释清除概念=>写用例=>写伪代码=>再实现具体功能,再优化,一步步来。
8. instanceof
是什么
instanceof 运算符用于检测构造函数的
prototype属性是否出现在某个实例对象的原型链上。
这个定义一出来,其实就是明确指导代码了,所以弄清定义非常重要。
几个重点
-
在 JavaScript 中,对象有一个特殊的隐藏属性
[[Prototype]],它要么为null,要么就是对另一个对象的引用。该对象被称为“原型”, 也可以理解为这个属性是指向原型对象的指针。 -
构造函数
F的prototype属性 在new F被调用时为新对象的[[Prototype]]赋值。
也就是说 rabbit 这个实例,它的构造函数是 Rabbit , 而它的原型是 Rabbit.prototype 属性。instanceof 就是检测这个原型是否存在于 rabbit这个实例对象的原型链上, 是,返回 true, 不是 返回 false
如果对原型不熟,看这里[核心概念] 一文说透JS中的原型和继承
简单手写实现
实现
- 写几个测试用例先
console.log(myInstanceof([], Array)) // true
console.log(myInstanceof([], Object)) // true
// 注意基本数据类型直接返回 false
console.log(myInstanceof("aaa", String)) // false
- 那我们就先写出伪代码,理清思路
function myInstanceof(left, right) {
// 特判,基本数据类型直接返回 false
if (基本类型) {
return false
}
// left 是被检验方,先获取它的原型对象 可以用 Object.getPrototypeOf
while(true) {
一层层往原型链顶端查,直到 null
中途找到 => return true
找不到 => {
再往原型链上层找
为 null 都没有则 false
}
}
}
- 实现主逻辑
function myInstanceof(left, right) {
// 基本数据类型直接返回 false
if (typeof left !== 'object' || left === null) {
return false
}
// getPrototypeOf 是 Object对象自带的一个方法,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left)
while (true) {
// 查找到尽头,还没找到
if (proto === null) {
return false
}
// 找到相同的原型对象
if(proto === right.prototype) {
return true;
}
proto = Object.getPrototypeOf(proto)
}
}
// console.log(myInstanceof([], Array))
9. new 关键字
是什么
分析
new 函数实现同样需要对原型这块深刻了解,建议读[核心概念] 一文说透JS中的原型和继承
然后我们必须知道 new Foo() 执行时,会发什么事情,这是 MDN 的说法
- 一个继承自
Foo.prototype的新对象被创建。 - 使用指定的参数调用构造函数
Foo,并将this绑定到新创建的对象。new Foo 等同于new Foo(),也就是没有指定参数列表,Foo不带任何参数调用的情况。 - 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤 1 创建的对象。
其实我建议看下面的实现步骤会更清楚
简单手写实现
实现
先写测试用例
// 一个学生类的构造函数
function Student (name, age) {
this.name = name;
this.age = age;
this.height = '180 cm';
}
// 我们在构造函数的原型对象上挂了一个身高属性
Student.prototype.weight = '60 kg';
// 再挂上一个 sayHello 方法,好处是该方法内存指向一处,因为是原型上的
Student.prototype.sayHello = function () {
console.log('hello ' + this.name);
}
我们先看原生 new 关键字效果
// 用 new 关键字 使用 Student 构造方法创建对象 boy1
var boy1 = new Student('Never', '18');
// 构造方法内部属性,包括原型链上的属性方法,都可以被该实例访问
console.log(boy1.name, boy1.age) // Never 18
console.log(boy1.height) // '180 cm'
console.log(boy1.weight) // 60 kg
boy1.sayHello(); // hello Never
我们需要实现一个类似效果
// 第一个参数传入这个 构造方法(Constructor),接着传入参数的调用方式创建实例
let boy2 = myNew(Student, 'More', '27')
// 需要实现 构造方法内部属性,包括原型链上的属性方法,都可以被boy2实例访问
console.log(boy2.name, boy2.age) // More 27
console.log(boy2.height) // 180 cm
console.log(boy2.weight) // 60 kg
boy2.sayHello() // hello More
详细步骤解析
下面我给你准备了一个完整的详细说明的 myNew
function myNew() {
// 1. 先创建一个空的对象
let obj = {}
// 2. 获取第一个参数 就是构造函数
// 写法同 Array.prototype.shift.call(arguments) 都为了借用 Array.prototype 上 shift 方法
// shift方法从前面获取第一个元素返回,并从数组中删除该元素,看执行后打印
let Constructor = [].shift.call(arguments);
// console.log(Constructor) // [Function: Student] 获取了第一个参数
// console.log(arguments) // { '0': 'More', '1': '27' } 余下参数
// 3. 将 obj 的[[Prototype]]指向构造函数的 prototype,这样 obj 就可以访问到构造函数原型中的属性
// 可以用 obj.__proto__ = Constructor.prototype; 方式
// 当然用下面这种写法更好,虽然效果相同 __proto__ 是 [[Prototype]] 的因历史原因而留下来的 getter/setter
Object.setPrototypeOf(obj, Constructor.prototype)
// 4. 使用 apply,改变构造函数 this 的指向到新建的对象,符合 new 关键字的this规则
let res = Constructor.apply(obj, arguments);
// 5. 如果函数没有返回对象类型Object,那么new表达式中的函数调用会自动返回这个新的对象(obj)。
// 如果构造函数返回了对象,则直接返回这个对象
return typeof res === "object" ? res : obj;
}
let boy2 = myNew(Student, 'More', '27')
console.log(boy2.name, boy2.age) // More 27
console.log(boy2.weight) // '180 cm'
console.log(boy2.height) // '60 kg'
boy2.sayHello() // hello More
我想这注释已经能解释绝大多数你的疑问了,如果还不明白评论区提问。
另外语言的发展,语法的支持后有很多地方可简化,原理相同,只不过用了新语法
function myNew(Constructor, ...args) {
// 1、创建一个空的对象并链接到原型对象,obj 可以访问构造函数原型中的属性和方法
// 这里用 Object.create 这个 api 来做 proto 链接
let obj = Object.create(Constructor.prototype);
// 严谨可判断下 Constructor.prototype === null ? Object.prototype : Constructor.prototype
// 2、绑定 this 实现继承,obj 可以访问到构造函数中的属性
let res = Constructor.apply(obj, args);
// 3、如果构造函数返回了对象,则直接返回这个对象,否则返回该新对象 obj
return typeof res === "object" ? res : obj;
};
let boy2 = myNew(Student, 'More', '27')
console.log(boy2.name, boy2.age) // More 27
console.log(boy2.weight) // '180 cm'
console.log(boy2.height) // '60 kg'
boy2.sayHello() // hello More
其中有个点是关于 Object.create,建议查看 MDN深入了解
另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我
presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧