javaScript 的设计模式与开发实践一(基础知识)
一、面向对象的javascript
javascript 没有提供传统面向对象语言中的类式继承,而是提供了基于原型委托(原型链)的方式实现对象与对象之间的继承。具体可以看下javaScript语言的一些特征。
1、动态类型语言和鸭子类型
编程语言按照数据类型可以分成:静态类型和动态类型。所谓的静态类型是编译时已经确定好变量的类型,编辑器在编译的时候就能够及时发现错误。而动态类型是在运行时,变量具体被赋予某个值后,才会具有某个类型。但动态类型给编码带来了很大的灵活性,基于鸭子类型(只要叫出来像‘嘎嘎嘎’,不管实际是鸭子,还是鸡),可以非常轻松的实现一个原则“面向接口编程,而不是面向实现编程”。
2、多态
多态的含义是统一操作作用于不同的对象上可以产生不同的解释和执行结果。背后的思想是将“做什么”和“谁去做以及怎样去做”分离来。其最根本的作用是通过把程序化的条件分支语句转化为对象的多态。绝大部份的设计模式离不开多态的思想。
3、封装
(1)封装数据:依赖变量的作用域来进行私有化的管理,比如利用let 和函数创建。es6中通过Symbol 创建私有属性。
(2)封装实现:对象之间只通过暴露的API接口来通信,不必关心其内部的实现。实现了对象之间的耦合变得松散。
(3)封装类型:属于静态语言一种重要的封装方式,一般通过抽象类和接口的形式。
(4)封装变化:将不变和变化的。
4、基于原型继承的Javascript 系统
原型模式是用于创建对象的一种模式。如果我们要创建对象,一个方法是:先指定它的类型,然后通过类来创建这个对象。而原型模式则不甘心对象的具体类型,直接克隆,复制一个一模一样的对象。 es6 中提供了Object.create 的方法。
javascript 中的原型继承满足以下规则:
(1)大部分的数据都是对象 javascript的根对象是Object.prototype 对象,它是一个空对象,是所有对象的原型。
(2)要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它
funtion Person (name){ this.name = name } var a = new Person 复制代码
这里的Person不是类,而是函数构造器。用new的运算符创建对象的过程实际上也是先克隆Object.prototype 对象。
(3)对象通过_proto_的隐藏属性去记住它的原型
(4)如果对象无法响应某个请求,它会把请求委托给他的构造器原型,通过原型链往上查找,最终找到 Object.prototype,如果原型是null,则返回undefined
(5)原型继承未来 通过构造函数创建的对象几乎都有一个原型对象,但通过Object.create(null),可以创造出没有原型的对象。同时,es6带来了新的创造对象的语法 class
二、this、call、apply
1、this
javaScript 的this 总指向一个对象,其是由函数执行的环境动态绑定的,而不是函数被声明时候的环境。
(1)函数作为对象的方法调用时:指向该对象。
(2)函数作为普通函数调用:this指向全局对象window对象,严格模式下为undefiend
(3)函数作为构造器调用:如果构造器不显示的返回对象类型的数据,则this 指向new 返回的这个对象,否则,指向构造器返回的这个对象。
(4)call和apply :改变this指向,指向传入那个对象参数。
(5)this指向丢失 改写函数,利用apply 指向
//普通写法 let getId = document.getElementById var div = getId ('div') console.log(div); //异常,因为内部指向的this,指向window //修改函数,改为自调用函数,并将document.getElementById 传入,指定this 指向 document.getElementById = function(func){ return function(){ return func.apply(document,arguments) } }(document.getElementById) let getId = document.getElementById var div = getId ('div') //输出div1 复制代码
2、call 和 apply
(1)第一个参数都指定函数体内this 的指向。apply第二个参数为数组或者类数组,不必关心具体有多少的参数传入函数。而call ,第二个参数则需要一一对应的关系。如果第一个传入的参数是null,则this 默认指向它的宿主对象。
(2)作用
改变this指向
借用其他对象的方法(借用构造函数,实现继承效果;利用Array.prototype的方法,操作arguments)
三、闭包和高阶函数
1、闭包
(1)含义:一般来说函数调用结束后内部的变量也会被来及回收机制销毁,但由于部分变量在外部仍存在引用,则延续了这个变量的生命周期。这种结构称之为闭包。
(2)作用:封装变量;延续局部变量的寿命 (经典面试题:for 循环的 i 的问题)
(3)闭包和内存泄漏 闭包确实会让一些数据无法及时销毁,但可以手动设置为null 释放。使用必包容易形成循环引用,如果闭包的作用域链保存一些dom 节点,就可能出现内训泄漏,这块和COM 的引用计数有关。
2、高阶函数
(1)函数作为参数
回调函数
Array.prototype.sort
[1,7,5].sort(function(a,b){ return a -b; }) //输出[1,5,7] 复制代码
(2)函数作为返回值输出
判断数据类型
var Type = {} for(var i = 0, type, type = ['String','Array','Number'][i++]){ (function(type){ Type['is'+type]= function (obj){ return Object.prototype.toString.call(obj) === '[object '+ type +']' } })(type) } Type.isString('str') // true Type.isArray([]) //true 复制代码
柯里化
函数节流代码实现 (函数频繁被调用)
var throttle = function (fn,interval){ var __self = fn, timer, firstTime = true; return function (){ var args = arguments, __me = this; //如果是第一次直接调用 if(firstTime){ __self.apply(__me,args); return firstTime = false } // 如果定时器还在,说明前一次延迟执行还没执行 if(timer){ return false } timer = setTimeout(function(){ clearTimeout(timer); timer = null __self.apply(__me,args); }, interval || 500) } } 复制代码
分时函数(页面一次性渲染大量的节点)
var timeChunk = function (ary, fn, count){ var len = ary.length t; var start = function(){ for(var i = 0, i < Math.min(count || 1, ary.length); i++){ var obj = ary.shift(); fn(obj); } } return function(){ t = setInterval(function(){ if(ary.length === 0){ return clearInterval (t); } start(); },200) } } 复制代码
惰性加载函数
全文章整理摘自《javaScript 设计模式和开发实践》曾探著
伪原创工具 SEO网站优化 https://www.237it.com/
作者:乙稀乙稀Xin
链接:https://juejin.cn/post/7036278333176283143