这是我参与更文挑战的第 1 天,活动详情查看 更文挑战
这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。
平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态、习惯、思路清晰程度等。
注意是简单实现,不是完整实现,重要的是概念清晰和实现思路清晰,建议
先解释清除概念
=>写用例
=>写伪代码
=>再实现具体功能
,再优化
,一步步来。
10. currying (柯里化)
是什么
柯里化 是一种转换,将 f(a,b,c)
转换为可以被以 f(a)(b)(c)
的形式进行调用。JavaScript 实现通常都保持该函数可以被正常调用,并且如果参数数量不足,则返回偏函数。
柯里化不会调用函数。它只是对函数进行转换。
关于柯里化概念的详细拆解分析请看 一文说透JS中的函数柯里化(Currying),这里我们只说简单手写实现,分析过程在这篇文章写的很清楚,强烈建议先阅读。
再次强调下,弄清定义非常重要,定义弄清伪代码写好,体现专业性。
简单手写实现
实现
- 写几个测试用例先
function sum(a, b, c) {
return a + b + c;
}
// 我们需要实现一个方法 myCurry 用来转换 sum 这个方法,形成 curriedSum
let curriedSum = myCurry(sum);
// curriedSum 这个方法可以按下面的方式部分调用
console.log( curriedSum(1, 2, 3) ); // 6,仍然可以被正常调用
console.log( curriedSum(1)(2, 3) ); // 6,对第一个参数的柯里化
console.log( curriedSum(1)(2)(3) ); // 6,全柯里化
console.log( curriedSum(1, 2)(3) ); // 6,部分调用
// 注意 方法的长度是 参数长度(a,b,c 3个参数)
console.log(sum.length) // 3
复制代码
- 实现主逻辑
function myCurry(func) {
// 我们myCurry调用应该返回一个包装器 curried,令这个函数curry化
return function curried(...args) {
// curry 的使用主要看参数数量, func.length 就是原函数func的参数数量
if (args.length >= func.length) {
// 如果传入的 args 长度与原始函数所定义的(func.length)相同或者更长,
// 那么只需要将调用传递给它即可。直接现在就调用,返回函数结果
return func.call(this, ...args)
} else {
// 否则的话,返回另一个包装器方法,递归地调用curried,将之前传入的参数与剩余新的参数拼接后一起传入。
return function pass(...rest) {
// 然后,在一个新的调用中,再次,我们将获得一个新的偏函数(如果参数不足的话),或者最终的结果。
return curried.call(this, ...args, ...rest)
}
}
}
}
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = myCurry(sum);
console.log( curriedSum(1, 2, 3) ); // 6,仍然可以被正常调用
console.log( curriedSum(1)(2, 3) ); // 6,对第一个参数的柯里化
console.log( curriedSum(1)(2)(3) ); // 6,全柯里化
console.log( curriedSum(1, 2)(3) ); // 6,部分调用
复制代码
11. Object.create()
是什么
MDN:
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
。
语法
Object.create(proto,[propertiesObject])
参数
- proto
- 新创建对象的原型对象。
- propertiesObject
- 可选。需要传入一个对象,该对象的属性类型参照
Object.defineProperties()
的第二个参数。如果该参数被指定且不为 undefined,该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。
- 可选。需要传入一个对象,该对象的属性类型参照
返回值
一个新对象,带着指定的原型对象和属性。
Object.create()
函数实现同样需要对原型这块深刻了解,建议读[核心概念] 一文说透JS中的原型和继承
new
和 Object.create
两者的区别主要在于
new
运算符只接受构造函数,而且不能构造没有原型的空对象;Object.create
可以接受constructor.prototype
还可以接受普通对象和null
(没有原型的空对象)。Object.create
如传入构造函数的prototype属性
,则不会继承原有构造函数中的属性;如为对象
,则会继承原对象的属性。
function Constructor(){}
obj = new Constructor();
// 上面的一句就相当于:
obj = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码
// 所以不会继承原有构造函数中的属性
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
obj = Object.create({}, { p: { value: 42 } })
// new 是无法创建 没有原型的对象的
复制代码
简单手写实现
实现
先写测试用例
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
// 这是原生
const me = Object.create(person);
// 我们要实现的
// const me = myObjectCreate(person);
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
复制代码
再实现
function myObjectCreate(proto, propertiesObject) {
// 特判,传入参数必须为对象 或者 null
if (typeof proto !== 'object') {
throw new TypeError('Object prototype may only be an Object');
}
// 内部新建一个构造函数 F
function F() {}
// 将构造函数的prototype属性指向传入的原型对象 proto
F.prototype = proto;
// 新建一构造函数的实例
var obj = new F();
// 如为 null, 需要再将实例的原型指向 null。否则,使用 new 新建实例时,会将原型指向 Object.prototype
if (proto === null) {
obj.__proto__ = null;
}
// 第二个参数的处理,为实例添加其他属性
if (propertiesObject) {
Object.defineProperties(obj, propertiesObject)
}
// 返回该实例
return obj;
};
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = myObjectCreate(person, { param: { value: 42 } });
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
console.log(me.param) // 42
复制代码
拓展
我们可以使用 Object.create
来实现比复制 for..in
循环中的属性更强大的对象克隆方式:
let a = {
c: 1,
b: {
d: 'aa'
}
}
const clone = (obj) => {
return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
}
let b = clone(a)
a.c = 2
a.b.d = 'change'
console.log(a) // { c: 2, b: { d: 'change' } }
console.log(b) // { c: 2, b: { d: 'change' } }
复制代码
此调用可以对 obj 进行真正准确地拷贝,包括所有的属性:可枚举和不可枚举的,数据属性和 setters/getters
—— 包括所有内容,并带有正确的 [[Prototype]]
。
两点注意
- 是浅copy
- for..in 不会枚举不可枚举类型的数据,并不会附带原型。
另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368
,可以聊天说地
加我暗号 “天王盖地虎” 下一句的英文
,验证消息请发给我
presious tower shock the rever monster
,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧