js面试题(一) JavaScript 手写代码无敌秘籍
Robin 10/27/2019 javaScript面试
# 手写路径导航
- 实现一个new操作符
- 实现一个JSON.stringify
- 实现一个JSON.parse
- 实现一个call或 apply
- 实现一个Function.bind
- 实现一个继承
- 实现一个JS函数柯里化
- 手写一个Promise(中高级必考)
- 手写防抖(Debouncing)和节流(Throttling)
- 手写一个JS深拷贝
- 实现一个instanceOf
# 1. 实现一个 new操作符
new
操作符做了这些事:
它创建了一个全新的对象。
它会被执行
[[Prototype]]
(也就是__proto__
)链接。它使
this
指向新创建的对象。通过
new
创建的每个对象将最终被[[Prototype]]
链接到这个函数的prototype
对象上。如果函数没有返回对象类型
Object
(包含Functoin,Array,Date,RegExg,Error
),那么 new表达式中的函数调用将返回该对象引用。
function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2)
// equals to
var obj = new A(1, 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2. 实现一个 JSON.stringify
JSON.stringify(value[,replacer[,space]])
:
Boolean|Number|String
类型会自动转换成对应的原始值。undefined
、任意函数以及symbol
,会被忽略(出现在非数组对象的属性值中时),或者被转换成null
(出现在数组中时)。不可枚举的属性会被忽略
如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略。
function jsonStringify(obj) {
let type = typeof obj;
if (type !== 'object' || type ===null) {
if (/string|undefined|function/.test(type)) {
obj = '"' + obj + '"'
}
return String(obj)
} else {
let json = []
arr = (obj && obj.constructor === Array);
for (let k in obj) {
let v = obj[k];
let type = typeof v;
if (/string|undefined|function/.test(type)) {
v = '"' + v + '"'
} else if (type === 'object') {
v = jsonStringify(v)
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? '[' : '{') + String(json) + (arr ? ']' : '}')
}
}
jsonStringify({x: 5}) // "{"x":5}"
jsonStringify([1, 'false', false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 3. 实现一个 JSON.parse
JSON.parse(text[,reviver])
1
用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的reviver函数用以在返回之前对所得到的对象执行变换(操作)。
# 4.实现一个call或 apply
# call
call
语法:
// 调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)
fun.call(thisArg,arg1,arg2,...)
1
2
2
- 分析:
- 确定上下文为
- 将函数设为对象的属性
- 执行&删除这个函数
- 指定
this
到函数并传入给定参数执行函数 - 如果不传入参数,默认指向
window
Function.prototype.myCall =
function(context = window, ...rest) {
// 上下文
context.fn = this
let ret ;
if(rest) {
ret = context.fn(...rest)
} else {
ret = context.fn()
}
delete context.fn;
return ret
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# apply
apply
语法:
// 调用一个函数,以及作为一个数组(或类似数组对象)提供的参数
func.apply(thisArg,[argsArray])
1
2
2
- 分析:
- 类似call,只是参数类型不同
Function.prototype.myApply =
function (context = window, ...rest) {
// 上下文context
// 扩展对象属性
context.fn = this
let ret;
// 判断是否有第二个参数
if (rest) {
ret = context.fn(...rest[0])
} else {
ret = context.fn()
}
delete context.fn
return ret
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 5.实现一个Function.bind
bind
语法:
// 会创建一个新函数。
// 当这个新函数被调用时,
// bind() 的第一个参数将作为它运行时的 this,
// 之后的一序列参数将会在传递的实参前传入作为它的参数
func.bind(args)
1
2
3
4
5
2
3
4
5
- 分析:
- 类似call,只是返回的是一个函数
Function.prototype.myBind = function(context, ...rest) {
return (...args) => {
// arguments此处不能使用
this.apply(context, [...rest, ...args])
}
}
1
2
3
4
5
6
2
3
4
5
6