前言
前端开发中原生JS的理解一定要足够透彻,这篇文章主要介绍面试过程中可能会出现的实现JS中的关键字或一些特殊方法的原理题。由于经常看了就忘,于是在此做下记录。
实现new
关键字 要实现new方法的原理,首先要知道new操作符具体干了什么
在js中,new操作符用于创建一个给定构造函数的实例对象。
1 2 3 4 5 6 7 8 9 10 11 function Person (name, age ) { this .name = name; this .age = age; } Person.prototype.sayName = function ( ) { console .log(this .name) } const person1 = new Person('Tom' , 20 )console .log(person1) t.sayName()
注:如果构造函数中存在返回值
返回值为基本数据类型,那么new创建的实例对象不会受返回值的结果影响。
返回值为引用数据类型(对象、数组),那么new创建的实例对象就是返回的数据结果。如下所示
1 2 3 4 5 6 7 8 function Test (name ) { this .name = name console .log(this ) return { age : 26 } } const t = new Test('xxx' )console .log(t) console .log(t.name)
new关键字实现创建对象的步骤:
创建一个空的实例对象 => obj
将实例对象与构造函数通过原型连接起来 => obj.__proto__ === 构造函数.prototype
将构造函数中的this绑定到实例对象obj上 => 构造函数.apply(obj, 参数)
判断构造函数的返回值类型;如果返回引用类型那么空对象赋值为构造函数返回结果,否则返回该对象。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 function myNew (Func, ...args ) { const obj = {} obj.__proto__ = Func.prototype let result = Func.apply(obj, args) return typeof result === 'object' ? result : obj }
1 2 3 4 5 6 7 8 9 10 function Person (name, age ) { this .name = name this .age = age } Person.prototype.sayHi = function ( ) { console .log("Hi, I'm" , this .name) } const lisi = myNew(Person, '李四' , 100 )console .log(lisi) lisi.sayHi()
call
、apply
、bind
的实现先说明以下三者的用途和区别
三者都可以改变函数的this对象指向
三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
bind执行后是返回绑定this之后的函数,apply、call 则是立即执行
使用方式如下:
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 function fn (...args ) { console .log('fn函数的this' , this ) console .log('fn函数的参数' , args) } console .log('------------fn------------' )fn(1 , 2 , 3 ) console .log('------------fn.apply------------' )fn.apply(obj, [1 , 2 , 3 ]) console .log('------------fn.call------------' )fn.call(obj, 1 , 2 , 3 ) console .log('------------fn.bind------------' )fn.bind(obj, 1 , 2 , 3 ) console .log('------------fn.bind返回值------------' )const bindReturn = fn.bind(obj, 1 )bindReturn()
实现call、apply和bind方法:
call方法的实现
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 28 29 30 31 32 33 34 35 function foo (...args ) { console .log('foo函数的this' , this ) console .log('foo函数的参数' , args) } var obj = { a: 1 , b: 2 } foo.call(obj, 1 , 2 ,3 ) obj = { a: 1 , b: 2 , foo: function (...args ) { console .log('foo函数的this' , this ) console .log('foo函数的参数' , args) } } Function .prototype.myCall = function ( ) { const context = [...arguments].slice(0 ,1 ) const rest = [...arguments].slice(1 ) if (!context) { context = typeof window === 'undefined' ? global : window } context.fn = this let result = context.fn(...rest) delete context.fn return result }
apply方法的实现
1 2 3 4 5 6 7 8 9 10 11 Function .prototype.myApply = function (context, args ) { if (!context) { context = typeof window === 'undefined' ? global : window } context.fn = this let result = args == undefined ? context.fn(args) : context.fn(...args) delete context.fn return result }
bind方法的实现
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 28 29 Function .prototype.myBind = function (context ) { if (typeof this !== 'function' ) { throw new TypeError ('Error' ) } const _self = this const args = [...arguments].slice(1 ) const Fn = function ( ) {} Fn.prototype = this .prototype const fnResult = function ( ) { const _this = this instanceof _self ? this : context const _args = [...args, ...arguments] return _self.apply(_this, _args) } fnResult.prototype = new Fn() return fnResult }
ajax
请求通过JS实现1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const xhr = new XMLHttpRequest()xhr.open('GET' , 'url' , true ) xhr.send() xhr.onreadyStateChange = function ( ) { if (xhr.readyState === 4 ){ if (xhr.status === 200 ){ ...处理成功的操作 }else { ...处理失败的操作 } } }
使用XMLHttpRequest对象封装ajax方法
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 function objToParams (obj ) { let paramsStr = '' for (const key in obj) { if (Object .hasOwnProperty.call(obj, key)) { const element = obj[key] paramsStr.length > 0 ? (paramsStr = paramsStr + '&' + `${key} =${element} ` ) : (paramsStr = `${key} =${element} ` ) } } return paramsStr } function ajax (request ) { request = request || {} const method = (request.method || 'GET' ).toUpperCase() const url = request.url const params = JSON .stringify(request.data) const xhr = new XMLHttpRequest() if (method === 'GET' ) { xhr.open(method, url + '?' + objToParams(params)) xhr.send() } else { xhr.open(method, url) xhr.send(params) } xhr.onreadystatechange = function ( ) { if (xhr.readyState === 4 ) { if (xhr.status === 200 ) { request.success && request.success(xhr.response) } else if (xhr.status >= 400 ) { request.fail && request.fail(xhr.responseText) } } } } ajax({ url: `http://jsonplaceholder.typicode.com/posts` , method: 'get' , success: function (res ) { console .log(res) }, fail: function (err ) { console .log(err) } })
深拷贝与浅拷贝实现 浅拷贝 浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。
浅拷贝实现的是拷贝对象的内存地址,改变拷贝对象的属性时,原对象属性也会随之改变。
实现代码如下:
1 2 3 4 5 6 7 8 9 function shallowClone (obj ) { const newObj = {} for (var key in obj){ if (obj.hasOwnProperty(key)){ newObj[key] = obj[key] } } return newObj }
在JavaScript
中,存在浅拷贝的现象有:
Object.assign
Array.prototype.slice()
, Array.prototype.concat()
使用拓展运算符实现的复制
深拷贝 深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
深拷贝实现1: JSON.stringify()。存在缺陷:会忽略undefined、symbol和函数类型的数据
1 2 3 4 function deepClone1 (obj ) { let newObj = JSON .parse(JSON .stringify(obj)) return newObj }
1 2 3 4 5 6 7 8 9 10 11 function deepClone2 (obj ) { if (obj === null || typeof obj !== 'object' ) return obj let newObj = new obj.constructor() for (let key in obj) { if (Object .hasOwnProperty.call(obj, key)) { newObj[key] = deepClone2(obj[key]) } } return newObj }
instanceof
关键字的实现
instanceof的用法:
instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
1 2 3 4 5 6 7 8 let Car = function ( ) {}let benz = new Car()benz instanceof Car let car = new String ('xxx' )car instanceof String let str = 'xxx' str instanceof String
实现instanceof的原理:
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 28 function instanceof2 (left, right ) { if (!['object' , 'function' ].includes(typeof left) || left === null ) return false let proto = Object .getPrototypeOf(left) while (proto !== null ) { if (proto === right.prototype) return true proto = Object .getPrototypeOf(proto) } return false } Object .getPrototypeOf([1 ,2 ]) === Array .prototype const prototype1 = {}const object1 = Object .create(prototype1)Object .getPrototypeOf(object1) === prototype1 Object .getPrototypeOf( Object ) === Function .prototype
拓展:结合typeof和instanceof精确判断某一个变量的数据类型
实现通用的获取数据类型方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function getType (obj ) { let type = typeof obj; if (type !== "object" ) { return type; } return Object .prototype.toString.call(obj).replace(/^\[object (\S+)\]$/ , '$1' ); } getType([]) getType('123' ) getType(window ) getType(null ) getType(undefined ) getType() getType(function ( ) {}) getType(/123/g )
Promise
实现原理promise
的使用
promise
对象一共只有三种状态:
1、pending
;2、fulfilled
;3、rejected
。
状态只允许从pending
变化成fulfilled
或从pending
变化成rejected
,且状态一旦改变,就无法回退。
通过promise的构造函数可以创建一个异步方法实例
1 const promise = new Promise (function (resolve, reject ) {});
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”
reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”
promise
对象常用的方法有then
、catch
、finally
这里主要介绍then方法的使用,在实现Promise的原理时也着重实现then方法:
then
是实例状态发生改变时的回调函数,第一个参数是resolved
状态的回调函数,第二个参数是rejected
状态的回调函数
then
方法返回的是一个新的Promise
实例,也就是promise
能链式书写的原因
1 2 3 4 5 getJSON("/posts.json" ).then(function (json ) { return json.post; }).then(function (post ) { });
开始实现promise
原理 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 class MyPromise { state = 'pending' result = undefined reason = undefined handleResolved = [] handleRejected = [] constructor (execute ) { const resolve = result => { if (this .state === 'pending' ) { this .state = 'fulfilled' this .result = result this .handleResolved.forEach(fn => fn && fn(result)) } } const reject = reason => { if (this .state === 'pending' ) { this .state = 'rejected' this .reason = reason this .handleRejected.forEach(fn => fn && fn(reason)) } } try { execute(resolve, reject) } catch (error) { reject(error) } } then (onResolved, onRejected ) { if (typeof onResolved !== 'function' ) { onResolved = data => data } if (typeof onRejected !== 'function' ) { onRejected = err => { throw new Error (err) } } const newPromise = new MyPromise((resolve, reject ) => { if (this .state === 'pending' ) { this .handleResolved.push(() => { const returnResult = onResolved(this .result) handleReturnPromise(returnResult, newPromise, resolve, reject) }) this .handleRejected.push(() => { const returnResult = onRejected(this .reason) handleReturnPromise(returnResult, newPromise, resolve, reject) }) } if (this .state === 'fulfilled' ) { setTimeout (() => { const returnResult = onResolved(this .result) handleReturnPromise(returnResult, newPromise, resolve, reject) }, 0 ) } if (this .state === 'rejected' ) { setTimeout (() => { const returnResult = onRejected(this .reason) handleReturnPromise(returnResult, newPromise, resolve, reject) }, 0 ) } }) return newPromise } catch (onRejected) { return this .then(undefined , onRejected); } } function handleReturnPromise (result, newPromise, resolve, reject ) { if (result === newPromise) { throw new Error ('return oneself is not allowed' ) } if (typeof result === 'object' || typeof result === 'function' ) { const then = result.then; if (typeof then === 'function' ) { then.call(result, (res ) => { handleReturnPromise(res, newPromise, resolve, reject) }, rej => { reject(rej) }) } else { resolve(result) } } else { resolve(result) } } const p = new MyPromise((resolve, reject ) => { setTimeout (() => { resolve('data' ) }, 1000 ) }) p.then((res, err ) => { console .log(res) console .log(err) return '第一层promise的返回值' }).then(res => { console .log(res) return new Promise ((resolve, reject ) => { setTimeout (() => { resolve('第二层Promise的返回值,我在2秒后出现' ) }, 1000 ) }) }).then(res => { console .log(res); })