js基础 手写const 回到var和let/const的区别上:
var声明的变量会挂到window上,而let和const不会 var声明的变量存在变量提升,而let和const不会 let和const声明形成块作用域,只能在块作用域里访问,不能跨块访问,也不能跨函数访问 同一作用域下let和const不能声明同名变量,而var可以 暂时性死区,let和const声明的变量不能在声明前被使用
实现const的关键在于Object.defineProperty()这个API,这个API用于在一个对象上增加或修改属性。通过配置属性描述符,可以精确地控制属性行为。Object.defineProperty() 接收三个参数:Object.defineProperty(obj, key, desc)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var __const = function __const (data, value ) { window .data = value Object .defineProperty(window , data, { enumerable: false , configurable: false , get : function () { return value }, set : function (data) { if (data !== value) { throw new TypeError ('Assignment to constant variable.' ) } else { return value } } }) } __const('a' , 10 ) console .log(a)delete aconsole .log(a)
mock 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 class MyPromise { constructor (executor) { this .cbs = []; this .data = undefined ; var resolve = (data ) => { setTimeout(() => { this .data = data this .cbs.forEach(cb => cb(data)) }) } executor(resolve) } then(onResolved, onReject) { return new MyPromise(resolve => { this .cbs.push(() => { const data = onResolved(this .data) if (data instanceof MyPromise) { data.then(resolve) } else { resolve(data) } }) }) } }
测试案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 new MyPromise((resolve ) => { console .log('start' ) setTimeout(() => { resolve(1 ); }, 500 ); }) .then((res ) => { console .log(res); return new MyPromise((resolve ) => { setTimeout(() => { resolve(2 ); }, 500 ); }); }) .then(console .log); console .log('end line' )
mock Promise.all Promise.all() 方法会将多个 Promise 实例组合成一个新的 Promise 实例 1.传入的参数必须可迭代 2.传入的实例不一定是Promise,必须再用Promise.resolve()包装下 3.组合后的Promise实例,只有当每个包含的Promise实例都解决才去解决(fulfilled),当然如果有一个Promise实例被拒绝的话,则合成的Promise会拒绝(rejected)
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 Promise .myAll = function (arr ) { return new Promise ((resolve, reject ) => { let res = [] for (let [i, item] of arr.entries()) { Promise .resolve(item).then( data => { res.push(data) if (i === arr.length -1 ) { resolve(res) } }, error => { reject(error) } ) } }) } var p1 = Promise .resolve( 1 );var p2 = Promise .resolve( Promise .resolve( '111' ) );var p3 = Promise .resolve('error' )Promise .all([p1, p2, p3]).then( res => { console .log(res) }, err => { console .warn(err) } ) Promise .myAll([p1, p2, p3]).then( res => { console .log(res,'my' ) }, err => { console .warn(err, 'muy' ) } )
Mock Promise.any Promise.all的反向操作,只有当每个包含的Promise实例都拒绝了,合成的promise才会拒绝rejected
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Promise .myAny = function (arr ) { return new Promise ((resolve, reject ) => { let res = [] for (let [index, item] of arr.entries()) { return Promise .resolve(item).then( (data) => { resolve(data) }, (err) => { if (index === arr.length -1 ) { reject(err) } res.push(err) } ) } }) }
mock Promise.race 只要有一个请求有响应值,请求就会结束,返回有响应的那个promise结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Promise .myAny = function (arr ) { return new Promise ((resolve, reject ) => { for (let [index, item] of arr.entries()) { return Promise .resolve(item).then( success: (data ) => { resolve(data) }, error: (err ) => { reject(err) } ) } }) }
mock Promise.allSettled(es7+) Promise.allSettled() 方法也是返回一个合成的 Promise,不过只有等到所有 Promise 实例都返回结果落定时,不管是解决(fulfilled)还是拒绝(rejected),合成的 Promise 才会结束。一旦结束,状态总是 fulfilled。
等所有的成功和失败的结果都有了才会返回promise结果,成功的时候返回[{value: '', status: 'fifulled'}]
,失败的时候返回[{reason: '', status: 'rejected'}]
,组成一个失败和成功的组合的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 Promise .prototype.myAllSettled = function (arr ) { return new Promise ((resove, reject ) => { let res for (let [index, item] of arr.entries()) { Promise .resolve(item).then( data => { res[i] = { value: data, status: 'fifulled' } if (index + 1 === arr.length) { resove(res) } }, error => { res[i] = { value: error, status: 'rejected' } if (index + 1 === arr.length) { resove(res) } } ) } }) } Promise .prototype.myAllSettled = function (arr ) { var PromiseAll = [...arr] return Promise .all(PromiseAll.map(e) => { return Promise .resolve(e).then( data => {status : 'fulfilled' , value : data}, err => {status : 'rejected' , reason : err} ) }) }
实现一个并发控制器 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 async function asyncPool (limit, arr, fn ) { try { const res = [] let executing = [] for (let i = 0 ; i < arr.length; i ++) { var p1 = Promise .resolve().then(() => fn.call(this , i)) res.push(p1) if (limit <= arr.length) { const cur = Promise .resolve(p1).then(() => { return executing.splice(executing.indeOf(cur),1 ) }) executing.push(cur) if (executing.length >= limit) { await Promise .race(executing) } } } return Promise .all(res) } catch (err) { console .log(err) } } const timeFn = i => { return new Promise ((resolve ) => setTimeout(() => resolve(i), i) ) } var arr = [1000 , 1000 , 1000 , 1000 , 1000 , 1000 , 1000 , 1000 , 1000 , 1000 ]asyncPool(2 , arr2, null ).then((res ) => { console .log(`res` , res) })
mock async 1 2 3 4 5 6 7 8 9 10 const getData = () => new Promise (resolve => setTimeout(() => resolve("data" ), 1000 ))async function test ( ) { const data = await getData() console .log('data: ' , data); const data2 = await getData() console .log('data2: ' , data2); return 'success' } test().then(res => console .log(res))
手写async(22行)
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 function asyncGenerator (fn ) { return function ( ) { const gen = fn.apply(this , arguments ) return new Promise ((resolve, reject ) => { function step (key, arg ) { let generatorResult try { generatorResult = gen[key](arg) } catch (e) { reject(e) } const {value, done} = generatorResult; if (done) { return resolve(value) } else { return Promise .resolve(value).then( val => step('next' , val), err => step('throw' , err) ) } } step('next' ) }) } } var getData = () => new Promise ((resolve ) => setTimeout(() => resolve('data' ), 1000 ))var test = asyncGenerator( function * testG ( ) { const data = yield getData() console .log('data1: ' , data); const data2 = yield getData() console .log('data2: ' , data2); return 'success' } ) test().then(res => console .log(res, 'cpp' ))
封装一个类使对象可以被for of遍历 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 class MakeIterator { constructor (obj) { this .obj = obj; this .len = Object .keys(obj).length; this .index = 0 ; } [Symbol .iterator]() {return this ;} next() { if (this .index < this .len) { return { value: this .obj[this .index ++], done: false } } else { return { value: undefined , done: true } } } } var obj = { 0 : 'wmh' , 1 : 'cpp' , 2 : 'chendap' , length: 3 } for (var item of new MakeIterator(obj)) { console .log(item) }
Bind 一句话介绍: bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
返回一个新函数 阔以传入多个参数 难点主要是在于:一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Function .protptyoe.mockBind = function (context, ...rest ) { var self = this ; var fNOP = function ( ) {} var fbound = function (...arg ) { var args = [...rest, ...arg]; self.apply(this instanceof self ? this : context, args) } fNOP.prototype = this .prototype; fbound.prototype = new fNOP() return fbound } function sayName (name, age ) { console .log(this .value); console .log(name); console .log(age); } Function .prototype.mockBind = mockBind;var sayFnMock = sayName.mockBind(obj2, 'wmh' )var resMock = new sayFnMock('菜鸟Mock' )console .log(resMock, 'Mock的bind返回的值' )
感谢冴羽大佬提供的高水平文章-JavaScript深入之bind的模拟实现
Apply 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 .prototype.mockApply = function (context, args ) { var context = context || window ; var sym = Symbol (); context[sym] = this ; let result = eval ('context.sym(...args)' ) delete context[sym] return result } const obj = { name: 'cpp' } function sayName (name ) { alert(name) } Function .prototype.mockApply = mockApplysayName.mockApply(obj, ['菜鸟' ]) Function .prototype.mockCall = function (context, ...args ) { var context = context || window ; context.fn = this ; var result = eval ('context.fn(...args)' ); delete context.fn return result; }
new step one: 创建一个空对象 obj; step two: 将空对象的隐式原型(proto)指向构造函数的prototype。 step three: 使用 apply 改变 this 的指向 step four: 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接返回该对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function mockNew (con, ...rest ) { let obj = Object .create(con.prototype) || window ; let res = con.apply(obj, rest) return res instanceof Object ? res : obj; } function SayName (name ) { this .name = name } SayName.prototype.emit = function ( ) { console .log(name) } var test = new SayName('cpp' )console .log(test)var test1 = mockNew(SayName, 'cpp' )console .log(test1)
instanceof 判断一个实例是否是其父类或者祖先类型的实例。 instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype查找失败,返回 false
1 2 3 4 5 6 7 8 9 function mockInstanceof (target, origin ) { while (target) { if (target.__proto__ === origin.prototype) {return true } target = target.__proto__ } return false } console .log([1 ,2 ] instanceof Array ) mockInstanceof([1 ,2 ], Array )
curry 函数柯里化 第一种 参数固定(有局限,形参个数固定) 1 2 3 4 5 6 7 8 9 10 11 12 13 function curry (fn ) { let judge = (...args ) => { if (args.length === fn.length) { return fn(...args) } return (...arg ) => judge(...args, ...arg) } return judge } var add = (a, b, c ) => a + b + c;var addCurry = curry(add)addCurry(1 )(2 )(3 )
第二种,参数不固定(自由灵活) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function add (...arr ) { return arr.reduce((a, b ) => a + b) } function curry (fn ) { var arr = [] return function temp (...rest ) { if (rest.length) { arr.push(...rest); return temp; } else { const val = fn.apply(this , arr) arr = [] return val } } } var addFn = curry(add)var test = addFn(3 )(1 )(5 )(44 )()
实现一个方法进行求和 1 2 3 4 5 var foo = function (...args ) { } var f1 = foo(1 )(2 )(3 )f1.getValue()
答案 1 2 3 4 5 var foo = function (...args ) { var target = (...arg ) => foo(...[...args, ...arg]) target.getValue = () => args.reduce((a, b ) => a + b, 0 ) return target }
函数重载 因为ECMAScript函数的参数是由零或多个值的类数组表示的,没有函数签名(接收的参数的类型和数量),真正的重载是不能做到的。 在JavaScript中,同一个作用域,出现两个名字一样的函数,后面的会覆盖前面的,所以 JavaScript 没有真正意义的重载。 但是我们可以通过检查传入函数中参数的类型和数量来执行不同的操作,从而实现“函数重载”。 核心思想: 通过判断arguments 对象的 length 属性来确定有几个参数,然后执行什么操作
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 addMethod (obj, name, fn ) { var old = obj[name] obj[name] = function ( ) { let arg = Array .from(arguments ); if (fn.length === arg.length) { fn.apply(this , arg) } else if (typeof fn === 'function' ) { old.apply(this , arg) } } } var person = {userName : 'bear鲍的小小熊' }addMethod(person, 'show' , function ( ) { console .log(this .userName + '---->' + 'show1' ) }) addMethod(person, 'show' , function (str ) { console .log(this .userName + '---->' + str) }) addMethod(person, 'show' , function (a, b ) { console .log(this .userName + '---->' + (a + b)) }) person.show() person.show('bkl' ) person.show(10 , 20 )
防抖debounce 核心:采用异步线程setTimeout进行延迟执行,多次触发之后执行一次,典型场景就是防止多次提交的按钮
1 2 3 4 5 6 7 8 9 10 11 function debounce (fn, wait ) { let timer return function (...arg ) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn.apply(this , arg) }, wait) } }
节流throttle 核心:每间隔多少秒执行一次,使之频率变低,典型场景:滚动事件
1 2 3 4 5 6 7 8 9 10 11 12 function throttle (fn, wait ) { let pre = 0 return function (...args ) { let now = new Date ().getTime() let that = this if (now - pre > wait) { fn.apply(that, args) pre = now } } }
webpack Mock webpack,模拟webpack打包构建过程 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 const fs = require ('fs' );const path = require ('path' )const parser = require ('@babel/parser' )const traverse = require ('@babel/traverse' )const babel = require ('@babel/core' )const getModuleInfo = (file ) => { const body = fs.readFileSync(file, 'utf-8' ) const ast = parser.parse(body, { sourceType: 'module' }) const deps = {} traverse.default(ast, { ImportDeclaration({node}) { const dirname = path.dirname(file) const depsPath = node.source.value const abspath = './' + path.join(dirname, depsPath) deps[depsPath] = abspath; } }) const {code} = babel.transformFromAst(ast, null , { presets: ['@babel/preset-env' ] }) return { file, deps, code, } } const parseModules = (file ) => { const entry = getModuleInfo(file); const temp = [entry] for (let i = 0 ; i < temp.length; i ++) { const deps = temp[i].deps; if (deps) { for (const key in deps) { if (deps.hasOwnProperty(key)) { temp.push(getModuleInfo(deps[key])) } } } } const depsGraph = {} temp.forEach((moduleInfo ) => { depsGraph[moduleInfo.file] = { deps: moduleInfo.deps, code: moduleInfo.code, } }) return depsGraph } const bundle = (file ) => { const depsGraph = JSON .stringify(parseModules(file)); return `(function (graph) { function require(file) { var exports = {}; function absRequire(relPath) { console.log(relPath, 'relPath') return require(graph[file].deps[relPath]) } (function(require, exports, code){ console.log(code) eval(code) })(absRequire, exports, graph[file].code) return exports } require('${file} ') })(${depsGraph} )` } function emit (path ) { const content = bundle(path) const distPath = './dist' ; const bundlePath = './dist/bundle.js' fs.unlinkSync(bundlePath); fs.rmdirSync(distPath); fs.mkdirSync(distPath) fs.writeFileSync(bundlePath, content) }; emit('./src/index.js' )
plugin 手写一个给打包文件头部注入作者和描述信息的webpack plugin 实现
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 class MyInfoPlugin { constructor (options = { author: 'cpp' , description: 'plugin name' }) { this .options = options } get pluginName() { return MyInfoPlugin.name } apply(compiler) { compiler.hooks.emit.tapPromise( this .pluginName, async (compilation) => { const options = this .options; await new Promise ((resolve, reject ) => { const assets = compilation.assets; Object .keys(assets).forEach((asset ) => { let source = assets[asset].source(); let info = [] if (options.author) { info.push(`@Author: ${options.author} ` ) } if (options.description) { info.push(`@Description: ${options.description} ` ) } if (info.length) { info.push(`@Date: ${new Date ()} ` ) source = `/*\n ${info.join('\n ' )} \n*/\n ${source} ` } compilation.assets[asset].source = () => source; compilation.assets[asset].size = () => source.size; }) resolve('Success' ) reject('MyInfoPlugin 插件出问题咯' ) }) } ) } } module .exports = MyInfoPlugin
测试
1 2 3 4 5 6 7 8 9 10 const MyInfoPlugin = require ('../plugin/MyInfoPlugin' )module .exports = { plugins: [ new MyInfoPlugin({ author: 'chendapeng' , description: 'Test test description' }), ] }
loader 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const loaderUtils = require ('loader-utils' );const validateOptions = require ('schema-utils' );const schema = { type: 'object' , properties: { name: { type: 'string' } } } module .exports = function (source ) { const options = loaderUtils.getOptions(this ) validateOptions(schema, options, 'Example Loader' ) return source }
测试 三种使用方式,推荐resolveLoader种的alias模式以及匹配(test)单个 loader还有常用的npm link软连接的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 module .exports = { resolveLoader: { alias: { 'async-loader' : path.resolve(__dirname, '../loader/async-loader.js' ) } }, module : { rules: [ { test: /\.js$/ , exclude: '/node_modules/' , use: [ { loader: 'async-loader' , options: { name: 'test my loader cpp' } } ] } ] } }
splitChunks Webpack中一个提取或分离代码的插件,主要作用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件
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 module .exports = { configureWebpack:config => { return { optimization: { splitChunks: { chunks: 'async' , minSize: 30000 , maxSize: 0 , minChunks: 1 , maxAsyncRequests: 6 , maxInitialRequests: 4 , automaticNameDelimiter: '~' , cacheGroups: { vendors: { name: `chunk-vendors` , test: /[\\/]node_modules[\\/]/ , priority: -10 , chunks: 'initial' , enfore: false }, common: { name: `chunk-common` , minChunks: 2 , priority: -20 , chunks: 'initial' , reuseExistingChunk: true } } } } } } };
Vue-cli3中的默认配置:
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 module .exports = { configureWebpack:config => { return { optimization: { splitChunks: { chunks: 'async' , minSize: 30000 , maxSize: 0 , minChunks: 1 , maxAsyncRequests: 6 , maxInitialRequests: 4 , automaticNameDelimiter: '~' , cacheGroups: { vendors: { name: `chunk-vendors` , test: /[\\/]node_modules[\\/]/ , priority: -10 , chunks: 'initial' }, common: { name: `chunk-common` , minChunks: 2 , priority: -20 , chunks: 'initial' , reuseExistingChunk: true } } } } } } };
参考
vue vue instance vue 模板编译,正则解析 正则解析是真的不好记!!!
输入: render(-
, {msg: ‘chendap’, name: ‘wmh’}) 输出: ‘chendap-wmh’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function render (template, data ) { var reg = /\{\{(\w+)\}\}/ if (reg.test(template)) { const name = RegExp .$1. trim() template = template.replace(reg, data[name]) return render(template, data) } return template } render(`{{msg}}-{{name}}` , {msg : 'chendap' , name : 'wmh' }) function transform (word ) { return word.replace(/[-|_|@]([\w])/g , (match, p) => p.toUpperCase()) }
RegExp 是javascript中的一个内置对象。为正则表达式。RegExp.$1是RegExp的一个属性,指的是与正则表达式匹配的第一个 子匹配(以括号为标志)字符串,以此类推,RegExp.$2,RegExp.$3,..RegExp.$99总共可以有99个匹配
实现一个 Observer 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 function Observer (data ) { this .data = data this .walk(data) } Observer.prototype = { walk(data) { Object .keys(data).forEach(() => { this .defineReactive(data, key, data[key]) }) }, defineReactive(data, key, val) { let dep = new Dep() let childObj = observe(val) Object .defineProperty(data, key, { enumerable: true , configurable: true , get : function getter() { if (Dep.target) { console .log(Dep.target) dep.addSub(Dep.target) } return val }, set : function setter() { if (newVal === val) { return } val = newVal childObj = observe(newVal) dep.notify() }, }) }, } function observe (value, vm ) { if (!value || typeof value !== 'object' ) { return } return new Observer(value) } function Dep ( ) { this .subs = [] } Dep.prototype = { addSub(sub) { this .subs.push(sub) }, notify() { this .subs.forEach((sub ) => { sub.update() }) }, } Dep.target = null
实现一个 Watcher 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 Watcher (vm, exp, cb ) { this .cb = cb this .vm = vm this .exp = exp this .value = this .get() } Watcher.prototype = { update() { this .run() }, run() { let value = this .vm.data[this .exp] let oldVal = this .value if (value !== oldVal) { this .value = value this .cb.call(this .vm, value, oldVal) } }, get () { Dep.target = this let value = this .vm.data[this .exp] Dep.target = null return value }, }
vuex vue-router Array.prototype.map() 1.不改变原数组 2.回调参数以及返回值如何处理 3.context 可选。对象作为该执行回调时使用,传递给函数,用作”this”的值。如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var arr = [1 ,2 ,3 ,4 ,5 ]var obj = {name : 'cpp' }Array .prototype.mockMap = function (fn, context ) { var arr = Array .prototype.slice.call(this ); var mapArr = [] for (var i = 0 ; i< arr.length; i ++) { mapArr.push(fn.call(context, arr[i], i, this )) } return mapArr } var test = arr.mockMap(function (e ) { console .log(this , 'this指向obj' ) return e *2 }, obj)
Array.prototype.filter() 返回过滤后的数组
1 2 3 4 5 6 7 8 9 10 11 12 13 Array .prototype.Filter = function (fn, context ) { let arr = this ; let len = arr.length; let newArr = []; for (let i=0 ; i<len; i++){ if (i in arr){ if (fn.call(context, arr[i], i, arr)){ newArr.push(arr[i]) } } } return newArr; }
Array.prototype.some() 一旦有一个值满足条件,即能返回true,否则返回false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Array .prototype.mySome = function (fn, context ) { var arr = this for (let i = 0 ; i < arr.length; i++) { if (i in arr) { if (fn.call(context, arr[i], i, arr)) { return true } } } return false } var arr = [0 ,1 ,0 ,3 ,12 ]var res = arr.mySome(e => e > 3 ) console .log(res)
Array.prototype.find()
返回当前满足条件的那个数组项
Array.prototype.reduce()
初始值传不传 差距蛮大
返回值如何处理1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Array .prototype.mockReduce = function (fn, initialValue ) { var arr = Array .prototype.slice.call(this ); var res, startIndex res = initialValue ? initialValue : arr[0 ]; startIndex = initialValue ? 0 : 1 ; for (var i = startIndex; i < arr.length; i ++) { res = fn.call(null , res, arr[i], i, this ) } return res } function add2 (...arr ) { return arr.mockReduce((a, b, i, ar ) => { console .log(a, b, i, arr) return a + b; }) } add2(...[1 ,2 ,3 ,4 ])
Array.prototype.flat() 多维数组降到一维数组
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 function flatten (arr = [] ) { const stack = [...arr] const res = [] while (stack.length) { const next = stack.pop() if (Array .isArray(next)) { stack.push(...next) } else { res.push(next) } } return res.reverse() } function flatten (arr = [] ) { return arr.redece((prev, cur ) => { return prev.concat(Array .isArray(cur) ? flatten(cur) : cur) }, []) } function flatten (arr = [] ) { while (arr.some(Array .isArray)) { arr = [].concat(...arr) } return arr } flatten([1 , 2 , 3 , [4 ,5 ], 6 ])
去重 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var arr = [1 , 2 , 3 , 3 , 4 , 1 , 00 , 99 ,99 ]function unique (arr ) { return arr.reduce((prev, cur ) => { if (!prev.includes(cur)) { prev.push(cur) } return prev }, []) } var test = unique(arr)console .log(tes) function unique (arr ) { return arr.filter((item, index, arr ) => arr.indexOf(item) === index) }
URLSearchParams url参数解析 1 2 3 4 5 6 7 8 9 10 11 var str = new URL('http://ccing.com/develop.html?systemId=111&idc=cc' )function getParams (url ) { var params = new URLSearchParams(url.search); var obj = {}; for ([k, v] of params.entries()) { obj[k] = v } return obj } var test = getParams(str)console .log(test)
存在缺陷,如果url里含有hash值,会优先解析hash参数,第二种
1 2 3 4 5 6 7 8 9 10 11 12 var str = 'http://ccuning.com/d.html#/dashboard?reportId=1118&versionId=1.0&systemId=111' function getParams (url ) { var params = url.split('?' ).pop().split('#' ).shift().split('&' ); var obj = {}; for ([k, v] of params.entries()) { const [key, val] = v.split('=' ) obj[key] = val; } return obj } var test = getParams(str)console .log(test)
缓存memorize函数 第一种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var memorize = function (fn ) { const cache = {} return function (...args ) { const _args= JSON .stringify(args) return cache[_args] || (cache[_args] = fn.apply(this , args)) } } var add = function (a, b ) { console .log('函数缓存' ) return a + b; } var addFn = memorize(add)var a1 = addFn(2 , 6 )var a2 = addFn(2 , 6 )console .log(a1, a2)
第二种:
1 2 3 4 5 6 7 8 9 10 const dataCache = new Map ()async getData(url) { let key = url let data = dataCache.get(key) if (!data) { const res = await req.get(url) dataCache.set(key, data) } return data }
手写原始AJAX 使用AJAX最主要的两个特性做下列事:
在不重新加载页面的情况下发送请求给服务器。
接受并使用从服务器发来的数据。 具体步骤: step one: 创建 XMLHttpRequest 实例 step two: 声明接到响应后要做啥事 step three: 发送一个实际的请求,通过调用HTTP请求对象的 open() 和 send() 方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function makeGetRequest (url, cb ) { let httpRequst = new XMLHttpRequest(); httpRequest.onreadystatechange = () => { if (httpRequest.readyState === XMLHttpRequest.done) { if (httpRequest.status === 200 ) { cb(httpRequest.responsText) } } } httpRequest.open('GET' , url, true ) httpRequest.send(); httpRequset.abort() }
如果你使用 POST 数据,那就需要设置请求的MIME类型。比如,在调用 send() 方法获取表单数据前要有下面这个:1 2 3 4 5 6 7 8 9 10 11 12 function makePostRequest (url, cb, userName ) { let httpRequst = new XMLHttpRequest(); httpRequest.onreadystatechange = () => { if (httpRequest.readyState === XMLHttpRequest.done) { if (httpRequest.status === 200 ) { cb(httpRequest.responsText) } } } httpRequest.open('Post' , url, true ); httpRequest.setRequestHeader('Content-Type' , 'application/x-www-form-urlencoded' ); httpRequest.send('userName=' + encodeURIComponent (userName));
手写jsonp 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function jsonp (url, callBack ) { var funcName = 'jsonp_' + Date .now(); const script = document .createElement('script' ) script.src = url script.async = true script.type = 'text/javascript' document .body.appendChild(script) window [funcName] = function (data ) { callBack && callBack(data) delete window [funcName] document .body.removeChild(script) } } jsonp('http://127.0.0.1:8080/api' , function (res ) { console .log(res) })
RGB转16进制 1.| 运算符跟 & 的区别在于如果对应的位中任一个操作数为1 那么结果就是1。 2. << 运算符使指定值的二进制数所有位都左移指定次数,其移动规则:丢弃高位,低位补0即按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。
1 2 3 4 5 6 7 8 function RGBToHex (rgb ) { var rgbArr = rgb.split(/[^\d]+/ ); var color = rgbArr[1 ] << 16 | rgbArr[2 ] << 8 | rgbArr[3 ]; return `#${color.toString(16 )} ` } var test = 'rgb(255,255,255)' console .log(RGBToHex(test))
16进制转RGB 1 2 3 4 5 6 7 8 9 10 function HexToRgb (hex ) { var hexx = hex.replace('#' , '0x' ); var r = hexx >> 16 ; var g = hexx >> 8 & 0xff ; var b = hexx & 0xff ; return `rgb(${r} , ${g} , ${b} )` } var test = '#ffffff' console .log(HexToRgb(test))
实现一下es6的extends 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Parent { constructor (name) { this .name = name } getName() { return this .name } } class Child extends Parent { constructor (name, age) { super (name) this .age = age } }
手写发布订阅模式 理解vue事件绑定的核心
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 class EventEmitter { constructor () { this .events = {} } $on(type, cb) { if (!this .events[type]) { this .events[type] = [cb] } else { this .events.push(cb) } } $emit(type, data) { if (this .events[type] && this .events[type].length) { this .events[type].forEach(fn => { fn.call(this , data) }); } } $off(type, cb) { if (!this .events[type]) return false if (this .events[type] && type.length) { this .events[type] = this .events[type].filter(e => e != cb) } else if (type.length) { this .events[type] = [] } else { this .events = {} } } once(type, cb) { function fn ( ) { cb() this .$off(type, fn) } this .$on(type, fn) } } const event = new EventEmitter()const handler = (...rest ) => { console .log(`rest handler` , rest) } event.$on('click' , handler) event.$emit('click' , 1 ,2 ,3 ,4 ) event.$off('click' , handler) event.once("dbClick" , () => { console .log(123456 ); }); event.$emit("dbClick" ); event.$emit("dbClick" );
简单的算法 滑动窗口,无重复字符的最长子串 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s = “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var lengthOfLongestSubstring = function (s ) { if (s && !s.length) return 0 let arr = [] let max = 0 for (let i = 0 ; i < s.length; i++) { const num = arr.indexOf(s[i]) if (num > -1 ) { arr.splice(0 , num + 1 ) } arr.push(s[i]) max = Math .max(max, arr.length) } return max };
计算容器的面积 1 2 3 输入:[1 ,8 ,6 ,2 ,5 ,4 ,8 ,3 ,7 ] 输出:49 解释:图中垂直线代表输入数组 [1 ,8 ,6 ,2 ,5 ,4 ,8 ,3 ,7 ]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49 。
双指针解法1 2 3 4 5 6 7 8 9 10 11 12 13 14 var maxArea = function (height ) { var max = 0 var left = 0 var right = height.length - 1 while (left < right) { max = Math .max(max, Math .min(height[right], height[left]) * (right - left)) if (height[right] > height[left]) { left ++ } else { right -- } } return max };
实现一个带缓存的求阶乘函数 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 function factoialIterative (n ) { if (n < 0 ) return undefined let total = 1 for (let num = n; num > 1 ; num --) { total = total * num } return total } function factorial (n ) { if (n <= 1 ) { return 1 } else { return n * factorial(n - 1 ) } } var b = factorial(3 )console .log(b)function factorial (n ) { var m = new Map () var fn = (n ) => { if (n <= 1 ) return 1 if (m.has(n)) return m.get(n) let res = n * fn(n - 1 ) m.set(n, res) return res } return fn(n) } function factorialTail (n, total = 1 ) { if (n <= 1 ) return 1 return factorialTail(n-1 , n * total) }
实现一个带缓存斐波那契数列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function fibonacci (n ) { if (n < 1 ) return 0 if (n <= 2 ) return 1 return fibonacci(n - 1 ) + fibonacci(n - 2 ) } function fibonacciMemo (n ) { const memo = [0 , 1 ] const fibonacci = (n ) => { if (memo[n] != null ) return momo[n] return memo[n] = fibonacci(n - 1 , memo) + fibonacci(n - 2 , memo) } return fibonacci(n) }
LRU Cache 浏览器中的缓存是一种在本地保存资源副本,它的大小是有限的,当我们请求数过多时,缓存空间会被用满,此时,继续进行网络请求就需要确定缓存中哪些数据被保留,哪些数据被移除,这就是浏览器缓存淘汰策略,最常见的淘汰策略有 FIFO(先进先出)、LFU(最少使用)、LRU(最近最少使用)。 LRU(least recently used): 最近最少使用 缓存淘汰策略;根据数据的历史记录来进行淘汰数据,其核心思想是如果最近被访问过,那么将来被访问的几率也高,优先淘汰最近没有被访问的数据
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 class LRUCache { private caches: Map<number , number > private capacity:number ; constructor (capacity: number ) { this .caches = new Map() this .capacity = capacity } get (key: number ): number { if (this .caches.has(key)) { const val = this .caches.get(key) this .caches.delete(key) this .caches.set(key, val) return val } return -1 } put(key: number , value: number ): void { if (this .caches.has(key)) { this .caches.delete(key) } else if ( this .caches.size >= this .capacity ) { this .caches.delete(this .caches.keys().next().value) } this .caches.set(key, value) } }
验证回文字符串 示例 1: 输入: “A man, a plan, a canal: Panama” 输出: true
1 2 3 4 5 6 7 8 9 var isPalindrome = function (s ) { if (typeof s != 'string' ) return s = s.toString() if (s.length < 2 ) return true var r = /[^a-zA-Z0-9]/ig ; s = s.replace(r, '' ).toLowerCase() return s == s.split('' ).reverse().join('' ) }; isPalindrome('cppc' )
全排列 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], … ] 题解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var permute = nums => { var len = nums.length if (nums.length < 2 ) return nums var res = [] var path = [] var backTrack = (path, nums ) => { if (path.length === nums.length) { res.push(path) } for (let num of nums) { if (!path.includes(num)) { path.push(num) backTrack(path.slice(), nums) path.pop() } } } backTrack(path, nums) return res } permute([1 ,2 ,3 ,4 ])
驼峰转换 // 输入: content-type // 输出: contentType
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function camel (str ) { let ans = "" ; let upper = false ; for (let index = 0 ; index < str.length; index++) { const element = str[index]; if (element == '_' || element == '-' || element == '@' ) { upper = true ; } else { if (upper) { ans += element.toUpperCase(); } else { ans += element; } upper = false ; } } return ans; }; function transform (word ) { return word.replace(/[-|_|@]([\w])/g , (match, p) => p.toUpperCase()) }
反过来呢,比如ContentType需要转成content-type呢
1 2 3 4 5 6 7 8 下划线转驼峰 function revserTransform (word, mark = '-' ) { return word.replace(/([A-Z])/g , `${mark} $1` ).replace(/(\w)+g/ , mark).toLowerCase().slice(1 ) } revserTransform('ContentType' , '@' ) revserTransform('ContentType' , '-' )
二分法查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function search (nums: number [], target: number ): number { let max = nums.length - 1 let min = 0 while (max >= min) { if (nums[max] === target) { return max } else if (target < nums[max]) { max -- } else if (target > nums[min]){ min ++ } } return -1 };
第一家公司就跪了,这道题铭记在心
反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
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 var l2 = { val: 1 , next: { val: 2 , next: { val: 3 , next: { val: 4 , next: { val: 5 , next: null } } } } } function reverse (head ) { let cur = head let pre = null let next = null while (current) { next = cur.next cur.next = pre pre = cur cur = next } return pre }
删除链表的节点 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动
示例 1:
输入: head = [4,5,1,9], val = 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
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 function deleteNode (head, val ) { if (head.val === val) return head.next let pre = head; while (head.next) { if (head.next.val == val) { head.next = head.next.next return pre } head = head.next } return head }; var head = { val: 4 , next: { val: 5 , next: { val: 1 , next: { val: 9 , next: null } } } } deleteNode(head, 1 )
合并俩个有序链表 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1 2 3 4 5 6 7 8 9 10 11 12 13 function mergeTwoLists (l1, l2 ) { if (!l1) { return l2 } else if (!l2) { return l2 } else if (l1.val < l2.val) { l1.next = mergeTwoLists(l1.next, l2) return l1 } else { l2.next = mergeTwoLists(l2.next, l1) return l2 } }
最大子序和 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1: 输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。 示例 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function maxSubArray (nums ) { let ans = nums[0 ] let sum = 0 for (let num of nums) { if (sum > 0 ) { sum = sum + num } else { sum = num } ans = Math .max(ans, sum) } return ans } var nums = [-2 ,1 ,-3 ,4 ,-1 ,2 ,1 ,-5 ,4 ]maxSubArray(nums)
参考链接
感谢各位大佬的倾情奉献,小弟只是代码搬运工!