实现new的过程
new操作符做了这些事:
- 创立一个全新的对象
- 这个对象的
__proto__
要指向构造函数的原型prototype - 执行构造函数,应用
call/apply
扭转 this 的指向 - 返回值为
object
类型则作为new
办法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) { // 基于原型链 创立一个新对象 let newObj = Object.create(fn.prototype); // 增加属性到新对象上 并获取obj函数的后果 let res = fn.apply(newObj, args); // 扭转this指向 // 如果执行后果有返回值并且是一个对象, 返回执行的后果, 否则, 返回新创建的对象 return typeof res === 'object' ? res: newObj;}
// 用法function Person(name, age) { this.name = name; this.age = age;}Person.prototype.say = function() { console.log(this.age);};let p1 = myNew(Person, "poety", 18);console.log(p1.name);console.log(p1);p1.say();
实现 getValue/setValue 函数来获取path对应的值
// 示例var object = { a: [{ b: { c: 3 } }] }; // path: 'a[0].b.c'var array = [{ a: { b: [1] } }]; // path: '[0].a.b[0]'function getValue(target, valuePath, defaultValue) {}console.log(getValue(object, "a[0].b.c", 0)); // 输入3console.log(getValue(array, "[0].a.b[0]", 12)); // 输入 1console.log(getValue(array, "[0].a.b[0].c", 12)); // 输入 12
实现
/** * 测试属性是否匹配 */export function testPropTypes(value, type, dev) { const sEnums = ['number', 'string', 'boolean', 'undefined', 'function']; // NaN const oEnums = ['Null', 'Object', 'Array', 'Date', 'RegExp', 'Error']; const nEnums = [ '[object Number]', '[object String]', '[object Boolean]', '[object Undefined]', '[object Function]', '[object Null]', '[object Object]', '[object Array]', '[object Date]', '[object RegExp]', '[object Error]', ]; const reg = new RegExp('\\[object (.*?)\\]'); // 齐全匹配模式,type应该传递相似格局[object Window] [object HTMLDocument] ... if (reg.test(type)) { // 排除nEnums的12种 if (~nEnums.indexOf(type)) { if (dev === true) { console.warn(value, 'The parameter type belongs to one of 12 types:number string boolean undefined Null Object Array Date RegExp function Error NaN'); } } if (Object.prototype.toString.call(value) === type) { return true; } return false; }}
const syncVarIterator = { getter: function (obj, key, defaultValue) { // 后果变量 const defaultResult = defaultValue === undefined ? undefined : defaultValue; if (testPropTypes(obj, 'Object') === false && testPropTypes(obj, 'Array') === false) { return defaultResult; } // 后果变量,临时指向obj持有的援用,后续将可能被一直的批改 let result = obj; // 失去晓得值 try { // 解析属性档次序列 const keyArr = key.split('.'); // 迭代obj对象属性 for (let i = 0; i < keyArr.length; i++) { // 如果第 i 层属性存在对应的值则迭代该属性值 if (result[keyArr[i]] !== undefined) { result = result[keyArr[i]]; // 如果不存在则返回未定义 } else { return defaultResult; } } } catch (e) { return defaultResult; } // 返回获取的后果 return result; }, setter: function (obj, key, val) { // 如果不存在obj则返回未定义 if (testPropTypes(obj, 'Object') === false) { return false; } // 后果变量,临时指向obj持有的援用,后续将可能被一直的批改 let result = obj; try { // 解析属性档次序列 const keyArr = key.split('.'); let i = 0; // 迭代obj对象属性 for (; i < keyArr.length - 1; i++) { // 如果第 i 层属性对应的值不存在,则定义为对象 if (result[keyArr[i]] === undefined) { result[keyArr[i]] = {}; } // 如果第 i 层属性对应的值不是对象(Object)的一个实例,则抛出谬误 if (!(result[keyArr[i]] instanceof Object)) { throw new Error('obj.' + keyArr.splice(0, i + 1).join('.') + 'is not Object'); } // 迭代该层属性值 result = result[keyArr[i]]; } // 设置属性值 result[keyArr[i]] = val; return true; } catch (e) { return false; } },};
应用promise来实现
创立 enhancedObject
函数
const enhancedObject = (target) => new Proxy(target, { get(target, property) { if (property in target) { return target[property]; } else { return searchFor(property, target); //理论应用时要对value值进行复位 } }, });let value = null;function searchFor(property, target) { for (const key of Object.keys(target)) { if (typeof target[key] === "object") { searchFor(property, target[key]); } else if (typeof target[property] !== "undefined") { value = target[property]; break; } } return value;}
应用 enhancedObject
函数
const data = enhancedObject({ user: { name: "test", settings: { theme: "dark", }, },});console.log(data.user.settings.theme); // darkconsole.log(data.theme); // dark
以上代码运行后,控制台会输入以下代码:
darkdark
通过观察以上的输入后果可知,应用 enhancedObject
函数解决过的对象,咱们就能够不便地拜访一般对象外部的深层属性。
前端手写面试题具体解答
用正则写一个依据name获取cookie中的值的办法
function getCookie(name) { var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)')); if (match) return unescape(match[2]);}
- 获取页面上的
cookie
能够应用document.cookie
这里获取到的是相似于这样的字符串:
'username=poetry; user-id=12345; user-roles=home, me, setting'
能够看到这么几个信息:
- 每一个cookie都是由
name=value
这样的模式存储的 - 每一项的结尾可能是一个空串
''
(比方username
的结尾其实就是), 也可能是一个空字符串' '
(比方user-id
的结尾就是) - 每一项用
";"
来辨别 - 如果某项中有多个值的时候,是用
","
来连贯的(比方user-roles
的值) - 每一项的结尾可能是有
";"
的(比方username
的结尾),也可能是没有的(比方user-roles
的结尾) - 所以咱们将这里的正则拆分一下:
'(^| )'
示意的就是获取每一项的结尾,因为咱们晓得如果^
不是放在[]
里的话就是示意结尾匹配。所以这里(^| )
的意思其实就被拆分为(^)
示意的匹配username
这种状况,它后面什么都没有是一个空串(你能够把(^)
了解为^
它前面还有一个暗藏的''
);而|
示意的就是或者是一个" "
(为了匹配user-id
结尾的这种状况)+name+
这没什么好说的=([^;]*)
这里匹配的就是=
前面的值了,比方poetry
;刚刚说了^
要是放在[]
里的话就示意"除了^前面的内容都能匹配"
,也就是非的意思。所以这里([^;]*)
示意的是除了";"
这个字符串别的都匹配(*
应该都晓得什么意思吧,匹配0次或屡次)- 有的大佬等号前面是这样写的
'=([^;]*)(;|$)'
,而最初为什么能够把'(;|$)'
给省略呢?因为其实最初一个cookie
项是没有';'
的,所以它能够合并到=([^;]*)
这一步。 - 最初获取到的
match
其实是一个长度为4的数组。比方:
[ "username=poetry;", "", "poetry", ";"]
- 第0项:全量
- 第1项:结尾
- 第2项:两头的值
- 第3项:结尾
所以咱们是要拿第2项match[2]
的值。
- 为了避免获取到的值是
%xxx
这样的字符序列,须要用unescape()
办法解码。
解析 URL Params 为对象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';parseParam(url)/* 后果{ user: 'anonymous', id: [ 123, 456 ], // 反复呈现的 key 要组装成数组,能被转成数字的就转成数字类型 city: '北京', // 中文需解码 enabled: true, // 未指定值得 key 约定为 true}*/
function parseParam(url) { const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 前面的字符串取出来 const paramsArr = paramsStr.split('&'); // 将字符串以 & 宰割后存到数组中 let paramsObj = {}; // 将 params 存到对象中 paramsArr.forEach(param => { if (/=/.test(param)) { // 解决有 value 的参数 let [key, val] = param.split('='); // 宰割 key 和 value val = decodeURIComponent(val); // 解码 val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字 if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则增加一个值 paramsObj[key] = [].concat(paramsObj[key], val); } else { // 如果对象没有这个 key,创立 key 并设置值 paramsObj[key] = val; } } else { // 解决没有 value 的参数 paramsObj[param] = true; } }) return paramsObj;}
字符串最长的不反复子串
题目形容
给定一个字符串 s ,请你找出其中不含有反复字符的 最长子串 的长度。示例 1:输出: s = "abcabcbb"输入: 3解释: 因为无反复字符的最长子串是 "abc",所以其长度为 3。示例 2:输出: s = "bbbbb"输入: 1解释: 因为无反复字符的最长子串是 "b",所以其长度为 1。示例 3:输出: s = "pwwkew"输入: 3解释: 因为无反复字符的最长子串是 "wke",所以其长度为 3。 请留神,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。示例 4:输出: s = ""输入: 0
答案
const lengthOfLongestSubstring = function (s) { if (s.length === 0) { return 0; } let left = 0; let right = 1; let max = 0; while (right <= s.length) { let lr = s.slice(left, right); const index = lr.indexOf(s[right]); if (index > -1) { left = index + left + 1; } else { lr = s.slice(left, right + 1); max = Math.max(max, lr.length); } right++; } return max;};
验证是否是身份证
function isCardNo(number) { var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; return regx.test(number);}
字符串查找
请应用最根本的遍从来实现判断字符串 a 是否被蕴含在字符串 b 中,并返回第一次呈现的地位(找不到返回 -1)。
a='34';b='1234567'; // 返回 2a='35';b='1234567'; // 返回 -1a='355';b='12354355'; // 返回 5isContain(a,b);
function isContain(a, b) { for (let i in b) { if (a[0] === b[i]) { let tmp = true; for (let j in a) { if (a[j] !== b[~~i + ~~j]) { tmp = false; } } if (tmp) { return i; } } } return -1;}
实现redux中间件
简略实现
function createStore(reducer) { let currentState let listeners = [] function getState() { return currentState } function dispatch(action) { currentState = reducer(currentState, action) listeners.map(listener => { listener() }) return action } function subscribe(cb) { listeners.push(cb) return () => {} } dispatch({type: 'ZZZZZZZZZZ'}) return { getState, dispatch, subscribe }}// 利用实例如下:function reducer(state = 0, action) { switch (action.type) { case 'ADD': return state + 1 case 'MINUS': return state - 1 default: return state }}const store = createStore(reducer)console.log(store);store.subscribe(() => { console.log('change');})console.log(store.getState());console.log(store.dispatch({type: 'ADD'}));console.log(store.getState());
2. 迷你版
export const createStore = (reducer,enhancer)=>{ if(enhancer) { return enhancer(createStore)(reducer) } let currentState = {} let currentListeners = [] const getState = ()=>currentState const subscribe = (listener)=>{ currentListeners.push(listener) } const dispatch = action=>{ currentState = reducer(currentState, action) currentListeners.forEach(v=>v()) return action } dispatch({type:'@@INIT'}) return {getState,subscribe,dispatch}}//中间件实现export applyMiddleWare(...middlewares){ return createStore=>...args=>{ const store = createStore(...args) let dispatch = store.dispatch const midApi = { getState:store.getState, dispatch:...args=>dispatch(...args) } const middlewaresChain = middlewares.map(middleware=>middleware(midApi)) dispatch = compose(...middlewaresChain)(store.dispatch) return { ...store, dispatch } }// fn1(fn2(fn3())) 把函数嵌套顺次调用export function compose(...funcs){ if(funcs.length===0){ return arg=>arg } if(funs.length===1){ return funs[0] } return funcs.reduce((ret,item)=>(...args)=>ret(item(...args)))}//bindActionCreator实现function bindActionCreator(creator,dispatch){ return ...args=>dispatch(creator(...args))}function bindActionCreators(creators,didpatch){ //let bound = {} //Object.keys(creators).forEach(v=>{ // let creator = creator[v] // bound[v] = bindActionCreator(creator,dispatch) //}) //return bound return Object.keys(creators).reduce((ret,item)=>{ ret[item] = bindActionCreator(creators[item],dispatch) return ret },{})}
基于Promise.all实现Ajax的串行和并行
基于Promise.all实现Ajax的串行和并行
- 串行:申请是异步的,须要期待上一个申请胜利,能力执行下一个申请
- 并行:同时发送多个申请「
HTTP
申请能够同时进行,然而JS的操作都是一步步的来的,因为JS是单线程」,期待所有申请都胜利,咱们再去做什么事件?
Promise.all([ axios.get('/user/list'), axios.get('/user/list'), axios.get('/user/list')]).then(results => { console.log(results);}).catch(reason => {});
Promise.all并发限度及async-pool的利用
并发限度指的是,每个时刻并发执行的promise数量是固定的,最终的执行后果还是放弃与原来的
const delay = function delay(interval) { return new Promise((resolve, reject) => { setTimeout(() => { // if (interval === 1003) reject('xxx'); resolve(interval); }, interval); });};let tasks = [() => { return delay(1000);}, () => { return delay(1003);}, () => { return delay(1005);}, () => { return delay(1002);}, () => { return delay(1004);}, () => { return delay(1006);}];/* Promise.all(tasks.map(task => task())).then(results => { console.log(results);}); */let results = [];asyncPool(2, tasks, (task, next) => { task().then(result => { results.push(result); next(); });}, () => { console.log(results);});
const delay = function delay(interval) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(interval); }, interval); });};let tasks = [() => { return delay(1000);}, () => { return delay(1003);}, () => { return delay(1005);}, () => { return delay(1002);}, () => { return delay(1004);}, () => { return delay(1006);}];
JS实现Ajax并发申请管制的两大解决方案
tasks
:数组,数组蕴含很多办法,每一个办法执行就是发送一个申请「基于Promise
治理」
function createRequest(tasks, pool) { pool = pool || 5; let results = [], together = new Array(pool).fill(null), index = 0; together = together.map(() => { return new Promise((resolve, reject) => { const run = function run() { if (index >= tasks.length) { resolve(); return; }; let old_index = index, task = tasks[index++]; task().then(result => { results[old_index] = result; run(); }).catch(reason => { reject(reason); }); }; run(); }); }); return Promise.all(together).then(() => results);} /* createRequest(tasks, 2).then(results => { // 都胜利,整体才是胜利,按顺序存储后果 console.log('胜利-->', results);}).catch(reason => { // 只有有也给失败,整体就是失败 console.log('失败-->', reason);}); */
function createRequest(tasks, pool, callback) { if (typeof pool === "function") { callback = pool; pool = 5; } if (typeof pool !== "number") pool = 5; if (typeof callback !== "function") callback = function () {}; //------ class TaskQueue { running = 0; queue = []; results = []; pushTask(task) { let self = this; self.queue.push(task); self.next(); } next() { let self = this; while (self.running < pool && self.queue.length) { self.running++; let task = self.queue.shift(); task().then(result => { self.results.push(result); }).finally(() => { self.running--; self.next(); }); } if (self.running === 0) callback(self.results); } } let TQ = new TaskQueue; tasks.forEach(task => TQ.pushTask(task));}createRequest(tasks, 2, results => { console.log(results);});
实现bind办法
bind
的实现比照其余两个函数稍微地简单了一点,波及到参数合并(相似函数柯里化),因为bind
须要返回一个函数,须要判断一些边界问题,以下是bind
的实现
bind
返回了一个函数,对于函数来说有两种形式调用,一种是间接调用,一种是通过new
的形式,咱们先来说间接调用的形式- 对于间接调用来说,这里抉择了
apply
的形式实现,然而对于参数须要留神以下状况:因为bind
能够实现相似这样的代码f.bind(obj, 1)(2)
,所以咱们须要将两边的参数拼接起来 - 最初来说通过
new
的形式,对于new
的状况来说,不会被任何形式扭转this
,所以对于这种状况咱们须要疏忽传入的this
简洁版本
- 对于一般函数,绑定
this
指向 - 对于构造函数,要保障原函数的原型对象上的属性不能失落
Function.prototype.myBind = function(context = window, ...args) { // this示意调用bind的函数 let self = this; //返回了一个函数,...innerArgs为理论调用时传入的参数 let fBound = function(...innerArgs) { //this instanceof fBound为true示意构造函数的状况。如new func.bind(obj) // 当作为构造函数时,this 指向实例,此时 this instanceof fBound 后果为 true,能够让实例取得来自绑定函数的值 // 当作为一般函数时,this 指向 window,此时后果为 false,将绑定函数的 this 指向 context return self.apply( this instanceof fBound ? this : context, args.concat(innerArgs) ); } // 如果绑定的是构造函数,那么须要继承构造函数原型属性和办法:保障原函数的原型对象上的属性不失落 // 实现继承的形式: 应用Object.create fBound.prototype = Object.create(this.prototype); return fBound;}
// 测试用例function Person(name, age) { console.log('Person name:', name); console.log('Person age:', age); console.log('Person this:', this); // 构造函数this指向实例对象}// 构造函数原型的办法Person.prototype.say = function() { console.log('person say');}// 一般函数function normalFun(name, age) { console.log('一般函数 name:', name); console.log('一般函数 age:', age); console.log('一般函数 this:', this); // 一般函数this指向绑定bind的第一个参数 也就是例子中的obj}var obj = { name: 'poetries', age: 18}// 先测试作为结构函数调用var bindFun = Person.myBind(obj, 'poetry1') // undefinedvar a = new bindFun(10) // Person name: poetry1、Person age: 10、Person this: fBound {}a.say() // person say// 再测试作为一般函数调用var bindNormalFun = normalFun.myBind(obj, 'poetry2') // undefinedbindNormalFun(12) // 一般函数name: poetry2 一般函数 age: 12 一般函数 this: {name: 'poetries', age: 18}
留神:bind
之后不能再次批改this
的指向,bind
屡次后执行,函数this
还是指向第一次bind
的对象
实现模板字符串解析性能
let template = '我是{{name}},年龄{{age}},性别{{sex}}';let data = { name: '姓名', age: 18}render(template, data); // 我是姓名,年龄18,性别undefined
function render(template, data) { const reg = /\{\{(\w+)\}\}/; // 模板字符串正则 if (reg.test(template)) { // 判断模板里是否有模板字符串 const name = reg.exec(template)[1]; // 查找以后模板里第一个模板字符串的字段 template = template.replace(reg, data[name]); // 将第一个模板字符串渲染 return render(template, data); // 递归的渲染并返回渲染后的构造 } return template; // 如果模板没有模板字符串间接返回}
版本号排序的办法
题目形容:有一组版本号如下 ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']
。当初须要对其进行排序,排序的后果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
arr.sort((a, b) => { let i = 0; const arr1 = a.split("."); const arr2 = b.split("."); while (true) { const s1 = arr1[i]; const s2 = arr2[i]; i++; if (s1 === undefined || s2 === undefined) { return arr2.length - arr1.length; } if (s1 === s2) continue; return s2 - s1; }});console.log(arr);
实现一个治理本地缓存过期的函数
封装一个能够设置过期工夫的localStorage
存储函数
class Storage{ constructor(name){ this.name = 'storage'; } //设置缓存 setItem(params){ let obj = { name:'', // 存入数据 属性 value:'',// 属性值 expires:"", // 过期工夫 startTime:new Date().getTime()//记录何时将值存入缓存,毫秒级 } let options = {}; //将obj和传进来的params合并 Object.assign(options,obj,params); if(options.expires){ //如果options.expires设置了的话 //以options.name为key,options为值放进去 localStorage.setItem(options.name,JSON.stringify(options)); }else{ //如果options.expires没有设置,就判断一下value的类型 let type = Object.prototype.toString.call(options.value); //如果value是对象或者数组对象的类型,就先用JSON.stringify转一下,再存进去 if(Object.prototype.toString.call(options.value) == '[object Object]'){ options.value = JSON.stringify(options.value); } if(Object.prototype.toString.call(options.value) == '[object Array]'){ options.value = JSON.stringify(options.value); } localStorage.setItem(options.name,options.value); } } //拿到缓存 getItem(name){ let item = localStorage.getItem(name); //先将拿到的试着进行json转为对象的模式 try{ item = JSON.parse(item); }catch(error){ //如果不行就不是json的字符串,就间接返回 item = item; } //如果有startTime的值,阐明设置了生效工夫 if(item.startTime){ let date = new Date().getTime(); //何时将值取出减去刚存入的工夫,与item.expires比拟,如果大于就是过期了,如果小于或等于就还没过期 if(date - item.startTime > item.expires){ //缓存过期,革除缓存,返回false localStorage.removeItem(name); return false; }else{ //缓存未过期,返回值 return item.value; } }else{ //如果没有设置生效工夫,间接返回值 return item; } } //移出缓存 removeItem(name){ localStorage.removeItem(name); } //移出全副缓存 clear(){ localStorage.clear(); }}
用法
let storage = new Storage();storage.setItem({ name:"name", value:"ppp"})
上面我把值取出来
let value = storage.getItem('name');console.log('我是value',value);
设置5秒过期
let storage = new Storage();storage.setItem({ name:"name", value:"ppp", expires: 5000})
// 过期后再取出来会变为 falselet value = storage.getItem('name');console.log('我是value',value);
验证是否是邮箱
function isEmail(email) { var regx = /^([a-zA-Z0-9_\-])[email protected]([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/; return regx.test(email);}
实现一个JSON.stringify
JSON.stringify(value[, replacer [, space]]):
Boolean | Number| String
类型会主动转换成对应的原始值。undefined
、任意函数以及symbol
,会被疏忽(呈现在非数组对象的属性值中时),或者被转换成null
(呈现在数组中时)。- 不可枚举的属性会被疏忽如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
- 如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
function jsonStringify(obj) { let type = typeof obj; if (type !== "object") { if (/string|undefined|function/.test(type)) { obj = '"' + obj + '"'; } return String(obj); } else { let json = [] let arr = Array.isArray(obj) 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"}"
异步串行 | 异步并行
// 字节面试题,实现一个异步加法function asyncAdd(a, b, callback) { setTimeout(function () { callback(null, a + b); }, 500);}// 解决方案// 1. promisifyconst promiseAdd = (a, b) => new Promise((resolve, reject) => { asyncAdd(a, b, (err, res) => { if (err) { reject(err) } else { resolve(res) } })})// 2. 串行解决async function serialSum(...args) { return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))}// 3. 并行处理async function parallelSum(...args) { if (args.length === 1) return args[0] const tasks = [] for (let i = 0; i < args.length; i += 2) { tasks.push(promiseAdd(args[i], args[i + 1] || 0)) } const results = await Promise.all(tasks) return parallelSum(...results)}// 测试(async () => { console.log('Running...'); const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12) console.log(res1) const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12) console.log(res2) console.log('Done');})()
二叉树深度遍历
// 二叉树深度遍历class Node { constructor(element, parent) { this.parent = parent // 父节点 this.element = element // 以后存储内容 this.left = null // 左子树 this.right = null // 右子树 }}class BST { constructor(compare) { this.root = null // 树根 this.size = 0 // 树中的节点个数 this.compare = compare || this.compare } compare(a,b) { return a - b } add(element) { if(this.root === null) { this.root = new Node(element, null) this.size++ return } // 获取根节点 用以后增加的进行判断 放右边还是放左边 let currentNode = this.root let compare let parent = null while (currentNode) { compare = this.compare(element, currentNode.element) parent = currentNode // 先将父亲保存起来 // currentNode要不停的变动 if(compare > 0) { currentNode = currentNode.right } else if(compare < 0) { currentNode = currentNode.left } else { currentNode.element = element // 相等时 先笼罩后续解决 } } let newNode = new Node(element, parent) if(compare > 0) { parent.right = newNode } else if(compare < 0) { parent.left = newNode } this.size++ } // 前序遍历 preorderTraversal(visitor) { const traversal = node=>{ if(node === null) return visitor.visit(node.element) traversal(node.left) traversal(node.right) } traversal(this.root) } // 中序遍历 inorderTraversal(visitor) { const traversal = node=>{ if(node === null) return traversal(node.left) visitor.visit(node.element) traversal(node.right) } traversal(this.root) } // 后序遍历 posterorderTraversal(visitor) { const traversal = node=>{ if(node === null) return traversal(node.left) traversal(node.right) visitor.visit(node.element) } traversal(this.root) } // 反转二叉树:无论先序、中序、后序、层级都能够反转 invertTree() { const traversal = node=>{ if(node === null) return let temp = node.left node.left = node.right node.right = temp traversal(node.left) traversal(node.right) } traversal(this.root) return this.root }}
先序遍历
二叉树的遍历形式
// 测试var bst = new BST((a,b)=>a.age-b.age) // 模仿sort办法bst.add({age: 10})bst.add({age: 8})bst.add({age:19})bst.add({age:6})bst.add({age: 15})bst.add({age: 22})bst.add({age: 20})// 先序遍历// console.log(bst.preorderTraversal(),'先序遍历')// console.log(bst.inorderTraversal(),'中序遍历')// // console.log(bst.posterorderTraversal(),'后序遍历')// 深度遍历:先序遍历、中序遍历、后续遍历// 广度遍历:档次遍历(同层级遍历)// 都可拿到树中的节点// 应用访问者模式class Visitor { constructor() { this.visit = function (elem) { elem.age = elem.age*2 } }}// bst.posterorderTraversal({// visit(elem) {// elem.age = elem.age*10// }// })// 不能通过索引操作 拿到节点去操作// bst.posterorderTraversal(new Visitor())console.log(bst.invertTree(),'反转二叉树')
转化为驼峰命名
var s1 = "get-element-by-id"// 转化为 getElementByIdvar f = function(s) { return s.replace(/-\w/g, function(x) { return x.slice(1).toUpperCase(); })}
实现Array.isArray办法
Array.myIsArray = function(o) { return Object.prototype.toString.call(Object(o)) === '[object Array]';};console.log(Array.myIsArray([])); // true
转化为驼峰命名
var s1 = "get-element-by-id"// 转化为 getElementById
var f = function(s) { return s.replace(/-\w/g, function(x) { return x.slice(1).toUpperCase(); })}