单例模式
| class SingleObject{ |
| constructor(name) { |
| this.name = name |
| } |
| |
| login() { |
| console.log('name: ', this.name) |
| } |
| } |
| |
| SingleObject.getInstance = (() => { |
| let instance |
| |
| return (name) => { |
| if (!instance) { |
| instance = new SingleObject(name) |
| } |
| return instance |
| } |
| })() |
| |
| |
| const a = SingleObject.getInstance('yang') |
| const b = SingleObject.getInstance('guo') |
| |
| console.log(a === b) |
| console.log(a.login(), b.login()) |
| |
| |
| |
观察者模式
| |
| class Subject{ |
| constructor() { |
| this.subs = [] |
| } |
| |
| addSub(sub) { |
| this.subs.push(sub) |
| } |
| |
| notify() { |
| this.subs.map(sub => { |
| sub.update() |
| }) |
| } |
| } |
| |
| |
| class Observer{ |
| update() { |
| console.log('updating') |
| } |
| } |
| |
| |
| const subject = new Subject() |
| const observer = new Observer() |
| subject.addSub(observer) |
| subject.notify() |
| |
| |
| |
发布订阅模式
| class EventEmitter{ |
| constructor() { |
| this._eventPool = {} |
| } |
| |
| on(event, cb) { |
| this._eventPool.event ? |
| this._eventPool.event.push(cb) |
| : |
| this._eventPool[event] = [cb] |
| } |
| |
| off(event) { |
| delete this._eventPool[event] |
| } |
| |
| emit(event, ...args) { |
| if (this._eventPool[event]) { |
| this._eventPool[event].map(cb => { |
| cb(...args) |
| }) |
| } |
| } |
| |
| once(event, cb) { |
| this.on(event, (...args) => { |
| cb(...args) |
| this.off(event) |
| }) |
| } |
| } |
| |
| |
| const emitter = new EventEmitter() |
| |
| emitter.on('a', (aaa, bbb) => { |
| console.log('fffffff', aaa, bbb) |
| }) |
| |
| emitter.emit('a', 'windy', 'tom') |
| |
| emitter.once('b', (aaa, bbb) => { |
| console.log('fffffff', aaa, bbb) |
| }) |
| emitter.emit('b', 'windy', 'tom') |
| |
基于一个主题/事件通道,订阅者subscriber通过自定义事件订阅主题,发布者publisher通过发布主题事件的方式发布。
观察者模式和发布订阅模式区别
- 在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。
- 发布订阅模式相比观察者模式多了个主题/事件通道,订阅者和发布者不是直接关联的。
- 观察者模式两个对象之间有很强的依赖关系;发布/订阅模式两个对象之间的耦合度低。
函数柯里化
当我们没有重新定义toString与valueOf时,函数的隐式转换会调用默认的toString方法,它会将函数的定义内容作为字符串返回。
而当我们主动定义了toString/vauleOf方法时,那么隐式转换的返回结果则由我们自己控制了。其中valueOf的优先级会toString高一点。
柯里化好处:参数复用、延迟运行(返回函数,想什么时候运行什么时候运行)
| function currying() { |
| const [fn, ..._args] = [...arguments] |
| const cb = function() { |
| if (!arguments.length) { |
| return fn.apply(this, _args) |
| } |
| _args.push(...arguments) |
| return cb |
| } |
| |
| |
| cb.toString = fn.apply(this, _args) |
| |
| return cb |
| } |
| |
| |
| function add() { |
| return [...arguments].reduce((a, b) => a + b) |
| } |
| |
| const a = currying(add, 12, 24, 36) |
| console.log(a()) |
| |
实现一个add方法,使结果满足如下预期
| add(1)(2)(3) = 6; |
| add(1, 2, 3)(4) = 10; |
| add(1)(2)(3)(4)(5) = 15; |
| function add() { |
| let _args = [...arguments] |
| const _adder = function() { |
| _args.push(...arguments) |
| return _adder |
| } |
| |
| _adder.toString = function() { |
| return _adder.reduce((a, b) => a + b) |
| } |
| return _adder |
| } |
实现call和apply
| Function.prototype.myCall = function() { |
| let [context, ...args] = [...arguments] |
| if (!context) context = window |
| |
| context.fn = this |
| const res = context.fn(...args) |
| |
| delete context.fn |
| return res |
| } |
| |
| Function.prototype.myApply = function() { |
| let [context, args] = [...arguments] |
| if (!context) context = window |
| |
| context.fn = this |
| let res |
| if (args) { |
| res = context.fn(...args) |
| } else { |
| res = context.fn() |
| } |
| |
| delete context.fn |
| return res |
| } |
| |
| |
| const person = { |
| name: 'mike', |
| getName: function(a, b) { |
| console.log(this.name, a, b) |
| } |
| } |
| |
| function printName(a, b) { |
| console.log(this.name, a, b) |
| } |
| |
| printName.myCall(person, 'time', 'fly') |
| printName.myApply(person, ['time', 'fly']) |
实现bind
| Function.prototype.myBind = function() { |
| const [context, ...args] = [...arguments] |
| const _this = this |
| return function() { |
| return _this.apply(context, args.concat(...arguments)) |
| } |
| } |
| |
| const person = { |
| name: 'mike', |
| getName: function(a, b) { |
| console.log(this.name, a, b) |
| } |
| } |
| |
| |
| const boy = { |
| name: 'boy' |
| } |
| |
| const getName2 = person.getName.myBind(boy, 'hhhh', 'yyyy') |
| getName2() |
实现instanceof
| function myInstanceof(left, right) { |
| let leftVal = left.__proto__ |
| let rightVal = right.prototype |
| |
| while (true) { |
| if (leftVal === null) return false |
| if (leftVal === rightVal) return true |
| leftVal = leftVal.__proto__ |
| } |
| } |
| |
new的本质
| function myNew(fun) { |
| return function() { |
| const obj = { |
| __proto__: fun.prototype |
| } |
| |
| fun.call(obj, ...arguments) |
| return obj |
| } |
| } |
| |
| |
| function Person(name, age) { |
| this.name = name |
| this.age = age |
| } |
| |
| const obj = myNew(Person)('yang', 18) |
| |
| |
Object.create的基本原理
| function myCreate(obj) { |
| function F() {} |
| F.prototype = obj |
| return new F() |
| } |
| |
实现promise
| class MyPromise{ |
| constructor(process) { |
| this.status = 'pending' |
| this.msg = '' |
| process(this.resolve.bind(this), this.reject.bind(this)) |
| return this |
| } |
| |
| resolve(val) { |
| this.status = 'fulfilled' |
| this.msg = val |
| } |
| |
| reject(val) { |
| this.status = 'rejected' |
| this.msg = val |
| } |
| |
| then(fulfilled, reject) { |
| if (this.status === 'fulfilled') { |
| fulfilled(this.msg) |
| } |
| if (this.status === 'rejected'){ |
| reject(this.msg) |
| } |
| } |
| } |
| |
| |
| const mm = new MyPromise((resolve, reject) => { |
| resolve('123') |
| }) |
| |
| mm.then(res => { |
| console.log(res, 'success') |
| }) |
| |
防抖和节流
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。
区别:
- 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。
- 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
应用:进行窗口的resize、scroll,输入框内容校验或请求ajax时
| |
| function debounce(fn, time) { |
| let timeout |
| return function() { |
| const _this = this |
| const args = arguments |
| |
| if (timeout) clearTimeout(timeout) |
| timeout = setTimeout(() => { |
| fn.apply(_this, args) |
| }, time || 500) |
| } |
| } |
| |
| |
| function throttle(fn, time) { |
| let timeout |
| return function() { |
| const _this = this |
| const args = arguments |
| |
| if (!timeout) { |
| timeout = setTimeout(() => { |
| fn.apply(_this, args) |
| timeout = null |
| }, time || 500) |
| } |
| } |
| } |
| |
| |
| |
| function count() { |
| console.log('counting') |
| } |
| |
| window.onscroll = debounce(count, 1000) |
| window.onscroll = throttle(count, 1000) |
| |
| |
for循环和reduce实现map和filter
| |
| Array.prototype.map2 = function() { |
| const arr = this |
| const [fn, thisArg] = [...arguments] |
| let res = [] |
| |
| for ( let i = 0; i < arr.length; i++) { |
| res.push(fn.call(thisArg, arr[i], i, arr)) |
| } |
| return res |
| } |
| |
| |
| Array.prototype.map3 = function() { |
| const arr = this |
| const [fn, thisArg] = [...arguments] |
| |
| return arr.reduce((acc, cur, i) => { |
| acc.push(fn.call(thisArg, cur, i, arr)) |
| return acc |
| }, []) |
| } |
| |
| |
| const m = [1,2,3,4,54].map2(item => item * item) |
| console.log(m) |
| |
| |
| Array.prototype.filter2 = function() { |
| const arr = this |
| const [fn, thisArg] = [...arguments] |
| let res = [] |
| |
| for ( let i = 0; i < arr.length; i++) { |
| if (fn.call(thisArg, arr[i], i, arr)) { |
| res.push(arr[i]) |
| } |
| } |
| return res |
| } |
| |
| |
| Array.prototype.filter3 = function() { |
| const arr = this |
| const [fn, thisArg] = [...arguments] |
| |
| return arr.reduce((acc, cur, i) => { |
| fn.call(thisArg, cur, i, arr) && acc.push(cur) |
| return acc |
| }, []) |
| } |
| |
| |
| const n = [0, 1, 2, 3, 4, 5] |
| const n2 = n.filter3(item => item % 2) |
| console.log(n2) |
使用for循环打印1-10, 每个数字出现间隔500ms
错误方法:
| |
| for (var i = 1; i <= 10; i++) { |
| setTimeout((function(i) { |
| console.log(i); |
| })(i), 500); |
| } |
| |
正确方法:
| |
| for(var i=1;i<=10;i++){ |
| (function(i){ |
| setTimeout(function(){ |
| console.log(i); |
| },500 * i); |
| })(i); |
| } |
| |
或者使用let,let本身就是块级作用域
| for(let i=1;i<=10;i++){ |
| setTimeout(function(){ |
| console.log(i); |
| },500 * i); |
| } |
使用setTimeout模拟setInterval
| function fn() { |
| console.log('123') |
| } |
| |
| setTimeout(function f() { |
| fn() |
| setTimeout(f, 500) |
| }, 500) |
ES5实现继承
| function Parent() { |
| this.name = 'parent' |
| this.play = [1, 2, 3] |
| } |
| |
| function Child() { |
| Parent.call(this) |
| this.name = 'child' |
| } |
| |
| Child.prototype = Object.create(Parent.prototype) |
| Child.prototype.constructor = Child |
| |
| |
| const ss = new Child() |
| console.log(ss instanceof Child, ss instanceof Parent) |
| console.log(ss.constructor) |
| |