金九银十过了大半,笔者最近也面了一些公司,现将一些自己遇到的和收集的基础题目整理出来,后续会整理分享一些其他的信息,希望对你能有所帮助
闲言少叙,看正文
实现Object.create
创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 —MDN
1function create(obj) {
2 function Fun(){
3
4 }
5 Func.prototype = obj;
6 Func.prototype.constructor = Func;
7 return new Fun();
8}
实现instanceof方法
instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
1function myInstanceof(obj, ctor) {
2 let proto = Object.getPrototypeOf(obj);
3 let prototype = ctor.prototype;
4 where(true) {
5 if(!proto) return false;
6 if(proto === prototype) return true;
7 proto = Object.getPrototypeOf(proto);
8 }
9}
实现new关键字
在调用new之后会发生这几个步骤
-
创建一个空对象
-
设置原型:将空白对象的原型设置为函数的prototype对象
-
让函数的this指向这个对象,执行构造函数的代码(为空白对象添加属性)
-
判断函数的返回值
4.1. 如果是引用类型,直接返回,比如构造函数主动返回了一个对象:function T(){return {x: 1}}
4.2. 如果不是引用类型,返回空白对象; 比如构造函数返回一个数字:function T(){return 1}
1
2function objectFactory() {
3 let newObject = null;
4 let constructor = Array.prototype.shift.call(arguments);
5 let result = null;
6 if (typeof constructor !== 'function') {
7 console.error('type error');
8 return
9 }
10 newObject = Object.create(constructor.prototype);
11 result = constructor.apply(newObject, arguments);
12 let flag = result && (typeof result === 'function' || typeof result === 'object');
13 return flag ? result : newObject;
14}
拦截构造函数调用
1function Person(name) {
2 if (new.target !== undefined) {
3 this.name = name;
4 } else {
5 throw new Error('必须使用 new 命令生成实例');
6 }
7}
8
9
10function Person(name) {
11 if (new.target === Person) {
12 this.name = name;
13 } else {
14 throw new Error('必须使用 new 命令生成实例');
15 }
16}
17
18var person = new Person('张三');
19var notAPerson = Person.call(person, '张三');
实现继承
组合式继承
1
2function Father(name, age) {
3 this.name = name
4 this.age = age
5 this.hobby = ['敲代码', '解Bug', '睡觉']
6}
7
8Father.prototype.sayName = function () {
9 console.log(this.name, 666)
10}
11Father.prototype.x = 1
12
13function Child(name, age) {
14 Father.call(this, name, age)
15 this.a = 1
16}
17Child.prototype = Object.create(Father.prototype)
18
19
20function Super(foo) {
21 this.foo = foo
22}
23Super.prototype.printFoo = function() {
24 console.log(this.foo)
25}
26function Sub(bar) {
27 this.bar = bar
28 Super.call(this)
29}
30Sub.prototype = Object.create(Super.prototype)
31Sub.prototype.constructor = Sub
ES6版本继承
1class Super {
2 constructor(foo) {
3 this.foo = foo
4 }
5 printFoo() {
6 console.log(this.foo)
7 }
8}
9class Sub extends Super {
10 constructor(foo, bar) {
11 super(foo)
12 this.bar = bar
13 }
14}
简单实现Promise
这里简单实现一下,可以参考一下其他的Promise A+规范的实现,主要包含then,all,race
- then:
1const PENDING = 'pending';
2const RESOLVED = 'resolved';
3const REJECTED = 'rejected';
4
5function MyPromise(fn) {
6 const self = this;
7 this.state = PENDING;
8 this.value = null;
9 this.reason = null;
10 this.resolvedCallbacks = [];
11 this.rejectedCallbacks = [];
12
13 function resolve(value) {
14 if (value instanceof MyPromise) {
15 value.then(resolve, reject)
16 }
17
18 setTimeout(() => {
19 if (self.state === PENDING) {
20 self.state = RESOLVED;
21 self.value = value;
22 self.resolvedCallbacks.forEach(cb => cb(value));
23 }
24 }, 0)
25 }
26
27 function reject(reason) {
28 setTimeout(() => {
29 if (self.state === PENDING) {
30 self.state = REJECTED;
31 self.reason = reason;
32 self.rejectedCallbacks.forEach(cb => cb(reason));
33 }
34 }, 0)
35 }
36 try {
37 fn(resolve, reject);
38 } catch (e) {
39 reject(e);
40 }
41}
42
43MyPromise.prototype.then = function (onFulfilled, onReject) {
44 const self = this;
45 return new MyPromise((resolve, reject) => {
46 let fulfilled = () => {
47 try {
48 const result = onFulfilled(self.value);
49 return result instanceof MyPromise ? result.then(result) : resolve(result);
50 } catch (e) {
51 reject(e);
52 }
53 };
54 let rejected = () => {
55 try {
56 const result = onReject(self.reason);
57 return result instanceof MyPromise ? result.then(resolve, reject) : reject(result);
58 } catch (e) {
59 reject(e);
60 }
61 }
62 switch (self.state) {
63 case PENDING:
64 case RESOLVED:
65 case RESOLVED:
66
67 }
68 })
69}
70
71MyPromise.all = (promises) => {
72 return new MyPromise((resolve, reject) => {
73 if (!Array.isArray(promises)) {
74 throw new TypeError('arguments must be array');
75 }
76 let resolvedCounter = 0;
77 let promiseNum = promises.length;
78 let resolvedResult = [];
79
80 for (let i = 0; i < promises.length; i++) {
81 MyPromise.resolve(promises[i]).then(value => {
82 resolvedCounter++;
83 resolvedResult[i] = value;
84 if (resolvedCounter === promiseNum) {
85 return resolve(resolvedResult);
86 }
87 }, error => {
88 return reject(error);
89 })
90 }
91 })
92}
93MyPromise.race = function(args) {
94 return new Promise((resolve, reject) => {
95 for(let i = 0; len = args.length; i++) {
96 args[i].then(resolve, reject);
97 }
98 })
99}
防抖函数
防抖是n秒内会重新计时
1function debounce(fn, wait) {
2 let timer = null;
3 return function() {
4 if(timer) {
5 clearTimeout(timer);
6 timer = null;
7 }
8 timer = setTimeout(() => {
9 fn.apply(this, arguments);
10 }, wait);
11 }
12}
节流函数
n秒内不重新计时
1function throttle(fn, delay) {
2 let timer = null;
3 return function () {
4 if (timer) return;
5 timer = setTimeout(() => {
6 timer = null;
7 return fn.apply(this, arguments);
8 }, delay)
9 }
10}
实现类型判断函数
1function getType(value) {
2 if (value === null) {
3 return value + '';
4 }
5 if(typeof value === 'object') {
6 return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
7 } else {
8 return typeof value;
9 }
10}
实现call函数
执行步骤:
- 判断call的调用者是否为函数,不是函数需要抛出错误,call调用者就是上下文this,也就是需要被调用的函数
- 判断需要被调用的函数的的上下文对象是否传入,不存在就设置为window
- 处理传入的参数,截取第一个参数后的所有参数,作为被调用函数
- 将需要被调用的函数,绑在传入的上下文上,作为一个属性
- 使用传入的上下文调用这个函数,并返回结果
- 删除绑定的属性
- 返回结果
1
2Function.prototype.myCall = function(context) {
3 if(typeof this !== 'function') {
4 throw new TypeError('need function');
5 }
6 let args = arguments.slice(1);
7 let result = null;
8
9 context = context || window;
10
11 context.fn = this;
12 result = context.fn(...args);
13
14 delete context.fn;
15 return result;
16}
实现apply函数
唯一的不同就是最后参数的获取方式
1Function.prototype.myApply = function(context) {
2 if(typeof this !== 'function') {
3 throw new TypeError('need function');
4 }
5 let args = arguments[1];
6 let result = null;
7
8 context = context || window;
9
10 context.fn = this;
11 result = context.fn(...args);
12
13 delete context.fn;
14 return result;
15}
实现bind
-
先判断调用者是否为函数
-
缓存当前需要bind的函数,就是上面的调用者,也是是bind函数的上下文
-
返回一个函数,利用闭包原理实现对this的保存
-
函数内部用apply函数来处理函数调用
-
- 需要判断函数作为构造函数的情况,这个时候的this就是当前调用这个闭包函数的this
- 作为普通函数,直接使用传入的上下文就好了
1Function.prototype.myBind = function(context) {
2 if(typeof this !== 'function') {
3 throw new TypeError('need function');
4 }
5 let args = [...arguments].slice(1);
6 let fn = this;
7
8 return function F() {
9 return fn.apply(
10 this instanceof F ? this : context,
11 args.concat(...arguments)
12 )
13 }
14}
浅拷贝
1
2Object.assign(target, source1, source2);
3
4
5{...obj1, ...obj2}
6
7
8Array.prototype.slice
9Array.prototype.concat
10
11
12function shallowCopy(object) {
13 if(!object || typeof object !== 'object') return;
14 let newObj = Array.isArray(object);
15
16 for(let key in object) {
17 if(object.hasOwnProperty(key)) {
18 newObj[key] = object(key);
19 }
20 }
21 return newObj;
22}
深拷贝deepclone
可能的问题:
- json方法出现函数或symbol类型的值的时候,会失效
- 处理循环引用问题
- 处理可迭代类型的数据
- 处理包装类型
- 处理普通类型
简单版本参考vue版本:
- 判断类型是否为原始类型,如果是,无需拷贝,直接返回
- 为避免出现循环引用,拷贝对象时先判断存储空间中是否存在当前对象,如果有就直接返回
- 开辟一个存储空间,来存储当前对象和拷贝对象的对应关系
- 对引用类型递归拷贝直到属性为原始类型
1const deepClone = (target, cache = new WeakMap()) => {
2 if(target === null || typeof target !== 'object') {
3 return target
4 }
5 if(cache.get(target)) {
6 return target
7 }
8 const copy = Array.isArray(target) ? [] : {}
9 cache.set(target, copy)
10 Object.keys(target).forEach(key => copy[key] = deepClone(obj[key], cache))
11 return copy
12}
实现Object.assign
就是实现一个浅拷贝
1
2Object.myAssign = function (target, ...source) {
3 if (target === null) {
4 throw new TypeError('can not be null');
5 }
6 let ret = Object(target);
7 source.forEach(obj => {
8 if (!obj !== null) {
9 for (let key in obj) {
10 if (obj.hasOwnProperty(key)) {
11 ret[key] = obj[key];
12 }
13 }
14 }
15 });
16 return ret;
17}
简单实现async/await中的async函数
async/await语法糖就是使用Generator函数+自动执行器来运作的,注意只要要实现async函数就是实现一个generate函数+执行器的语法糖
1
2function getNum(num){
3 return new Promise((resolve, reject) => {
4 setTimeout(() => {
5 resolve(num+1)
6 }, 1000)
7 })
8}
9
10
11function asyncFun(func){
12 var gen = func();
13
14 function next(data){
15 var result = gen.next(data);
16 if (result.done) return result.value;
17 result.value.then(function(data){
18 next(data);
19 });
20 }
21
22 next();
23}
24
25
26var func = function* (){
27 var f1 = yield getNum(1);
28 var f2 = yield getNum(f1);
29 console.log(f2) ;
30};
31asyncFun(func);
实现一个Object.freeze
锁定对象的方法
- Object.preventExtensions()
no new properties or methods can be added to the project 对象不可扩展, 即不可以新增属性或方法, 但可以修改/删除
- Object.seal()
same as prevent extension, plus prevents existing properties and methods from being deleted 在上面的基础上,对象属性不可删除, 但可以修改
- Object.freeze()
same as seal, plus prevent existing properties and methods from being modified 在上面的基础上,对象所有属性只读, 不可修改
以上三个方法分别可用Object.isExtensible(), Object.isSealed(), Object.isFrozen()来检测
1var deepFreeze =function (obj) {
2 var allProps = Object.getOwnPropertyNames(obj);
3
4 allProps.forEach(item => {
5 if (typeof obj[item] === 'object') {
6 deepFreeze(obj[item]);
7 }
8 });
9 return Object.freeze(obj);
10}
模拟实现一个Object.freeze,使用了Object.seal
1 function myFreeze(obj) {
2 if (obj instanceof Object) {
3 Object.seal(obj);
4 let p;
5 for (p in obj) {
6 if (obj.hasOwnProperty(p)) {
7 Object.defineProperty(obj, p, {
8 writable: false
9 });
10 myFreeze(obj[p]);
11 }
12 }
13 }
14 }
用ES5实现一下map和reduce函数
1Array.prototype.myMap = (fn, context) => {
2 var arr = Array.prototype.slice.call(this);
3 var mapArray = [];
4 for (let i = 0; i < arr.length; i++) {
5 mapArray.push(fn.call(context, arr[i], i, this));
6 }
7 return mapArray;
8}
9
10Array.prototype.myReduce = (fn, initialValue) => {
11 var arr = Array.prototype.slice.call(this);
12 var res, startIndex;
13 res = initialValue ? initialValue : arr[0];
14 startIndex = initialValue ? 0 : 1;
15 for(let i = startIndex; i< arr.length; i++) {
16 res = fn.call(null, res, arr[i], i, this);
17 }
18 return res;
19}