实现instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)

要懂原型和原型链

function myInstanceof(target, origin) {
// 基本数据类型直接返回false
if (typeof target !== 'object' || target === null) return false
// 拿到 target 的__proto__
let proto = Object.getPrototypeOf(target)
// proto不为null就继续
while(proto) {
// 找到相同的原型对象
if (proto == origin.prototype) return true
// 继续沿着原型链找
proto = Object.getPrototypeOf(proto)
}
return false
}

console.log(myInstanceof("111", String)); //false
console.log(myInstanceof(new String("111"), String));//true

class People {}
class Student extends People {}
const stu = new Student();
console.log(stu instanceof People); // true
console.log(stu instanceof Student); // true

JS如何实现继承?

方式1:借助原型链

// 父类
function Person() {
this.name = 'pName'
this.age = 18
this.friends = []
}

Person.prototype.sayHello = function() {
console.log('Hello');
}

// 子类
function Student() {
this.height = 1.88
}

let p = new Person() // 获得Person类的属性方法
console.log(p);

// 我们让Student的原型指向p
Student.prototype = p

let stu = new Student()
console.log(stu.name); // pName
stu.sayHello() // Hello
// 可见我们已经拿到了父类的属性和方法

这种实现方法存在的问题:

1.当我们打印实例stu的时候,继承的属性不能直观看到,只能在__proto__属性中看到

console.log(stu)// Student {height: 1.88}
image.png

2.另外,如果我们创建两个实例对象,并改变friends属性

stu1.friends.push('aaa')
console.log(stu2.friends); // ['aaa']

明明改变的是stu1的属性,为什么stu2也变了?

因为我们它们的隐式原型指向的是同一个对象,p

3.前面实现的过程都没有传递参数

方式2:借助构造函数

使用call调用构造函数,传入的this为当前实例,并且可以把参数传给父类处理

// 父类
function Person(name, age) {
this.name = name
this.age = age
this.friends = []
}

Person.prototype.sayHello = function() {
console.log('Hello');
}

// 子类
function Student(name, age, height) {
Person.call(this, name, age) // 这里可以获得父类的属性
this.height = height
}

let p = new Person() // 依然需要这里来获得方法
Student.prototype = p

let stu1 = new Student('aaa', 18, 1.78)
let stu2 = new Student('bbb', 20, 1.88)

stu1.friends.push('aaa')
console.log(stu1);
console.log(stu2);
stu1.sayHello() // Hello

构造函数的方法可以解决原型链方式的问题,但是依然存在弊端

  1. Person 至少被调用两次(一开始new Person一次,后面Person.call又会调用Person)
  2. stu的原型对象上会多出一些属性, 但是这些属性是没有存在的必要(new Person的时候的)
image.png

方式3:寄生组合式继承(最终方案)

**Object.create()**方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

Object.create(XXX) 也就是传入的对象XXX,作为新对象的原型

// 父类
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}

Person.prototype.sayHello = function() {
console.log('Hello');
}

// 子类
function Student(name, age, friends, height) {
// 获取父类中的属性和方法
Person.call(this, name, age, friends)
this.height = height
}

// 再获取一份父类的prototype中的属性和方法
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student

let stu = new Student('aaa', 18, ['fre1'], 1.88)
console.log(stu);
image.png

当然,我们也可以封装一个方法来继承父类prototype中的属性和方法

function inheritPrototype(SubType, SuperType) {
SubType.prototype = Object.create(SuperType.prototype)

// 当然子类的prototype还需要有constructor指向子构造函数本身
Object.defineProperty(SubType.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: SubType
})
}
...
inheritPrototype(Student, Person)

JS柯里化实现

要求,传入一个函数,返回一个柯里化的函数

思路:当已经传入的参数 大于等于 所需要的参数时,就执行函数

没有达到个数时,需要返回一个新的函数,继续来收集参数,接收到参数后,递归调用curried来检查函数的个数是否达到

function myCurrying(fn) {
function curried(...args) {
// 当已经传入的参数 大于等于 需要的参数时, 就执行函数
if (args.length >= fn.length) {
// fn()
return fn.apply(this, args)
} else {
// 没有达到个数时, 需要返回一个新的函数,继续来接收参数
function curried2(...args2) {
// 接收到参数后, 需要递归调用curried来检查函数的个数是否达到
return curried.apply(this, [...args, ...args2])
}
return curried2
}
}

// 返回一个柯里化的函数
return curried
}

function add1(x, y, z) {
return x + y + z
}

var curryAdd = myCurrying(add1)
console.log(curryAdd(10, 20, 30));
console.log(curryAdd(10)(20, 30));
console.log(curryAdd(10)(20)(30));