声明: 【JavaScript设计模式】 系列 是来自《JavaScript设计模式与开发》这本书的读书笔记,会结合自身的理解和一些项目经验做笔记,原书作者 曾探
前言
什么是设计模式,设计模式就是在某种场合下对某个问题的一种解决方案。说通俗一点就是给一段代码起个名字而已,比如一个用玻璃做的能装水的东西,给它起个名字叫水杯
,
水杯
就知道什么对象,具有什么功能,这样大家一想到要喝水就找个水杯
这么个东西,满足需求 一、面向对象的JavaScript
1、动态类型语音
JavaScript是一门典型的动态类型语言。记住这一特性很重要,比如js有个数组类型Array,要是一个对象,这个对象具有length属性,还拥有slic,splice方法,那也可以将这个对象当做数组来用。这完全是可以的,因为语言是动态的,我们可以面向接口编程,而不是面向实现编程。
2、多态
也正因为js是动态类型语言,所以js本身具有多态的特性。
什么是多态呢
:多态就是同一操作作用于不同对象,产生不同的结果例子: function fun(obj) { obj.log()}function Obj1() {}Obj1.prototype.log = function () { console.log('....Obj1......')}function Obj2() {}Obj2.prototype.log = function () { console.log('....Obj2......')}fun(new Obj1()) // ....Obj1......fun(new Obj2()) // ....Obj2......
给函数 fun
传递不同对象,能得到不同的结果
3、封装
JavaScript是没有私有变量和共有变量或共有方法的,但它有个函数作用域,我们可以利用这一特性来满足这一点
例子:var fun2 = (function () { var _name = 'my name is 田生' return { name: _name, pubFun: function () { console.log('.....pubFun.....') } }})()console.log(fun2.name) // my name is 田生console.log(fun2.pubFun()) //.....pubFun.....
4、JavaScript中的原型继承
JavaScript的对象不是通过实例化得到的,而是通过克隆原型对象Object.prototype
得到的。
Object.prototype
对象,并把新对象附上属性值返回出去。例子: function Obj() { this.name = '田生'}var o1 = new Obj()console.log(o1.name) // 田生// 相当于function CloneObj() { var obj = new Object() // 从 `Object.prototype` 克隆一个新对象 obj.name = '田生' return obj}var o2 = CloneObj()console.log(o2.name) // 田生
5、JavaScript中的原型琏
如果对象无法响应某个请求时,它会把这个请求委托给它的构造器的原型prototype
去执行
function Obj() { this.name = '田生'}var o1 = new Obj()console.log(o1.toString()) // [object Object]console.log(o1)
上面例子克隆了个新的 对象Obj
,新对象没有 toString
方法,所以在调用 toString
方法时,这个请求就会委托给他的原型对象 Object
的 prototype
去执行
Obj对象
是怎么和 Object
的 prototype对象
挂上钩的呢?你可以试着在Chrome浏览器的开发者模式中输入上面代码,试着打印 console.log(o1)
会发现 Obj
有个 proto 的属性指向了 Object
对象,这就是它们连接的纽带 二、this、call 和 apply
1、this
当函数作为对象的方法调用时,this 指向改对象
例子:
var Obj = { name: '田生', fun: function () { console.log(this.name) // this 指向改 对象 }}Obj.fun() // 田生
当函数不作为对象的属性被调用时,也就是常说的普通方法,此时this指向全局对象
var name = '田生....2'function fun1() { console.log(this.name) // fun1 当做普通函数,this指向全局 function fun2() { console.log(this.name) // fun2 也当做普通函数,this指向全局 } fun2() // '田生....2'}fun1() // '田生....2'
fun2 函数的this为什么也指向全局呢?有必要再强调一下,当函数不作为对象的属性被调用时,也就是常说的普通方法,此时this指向全局对象!
当然在 ES6 strict 模式下 this为undefined2、call 和 apply
首先要明确一点,JavaScript 的 Function 实际上是功能完整的对象。那对象就可以调用方法。
所以在看到下面的例子就不要疑惑:function fun(para) {}fun.lengthfun.apply(null, ['田生'])fun.call(null, '田生')fun.toString()
为什么一个函数有属性呢,为什么一个函数居然可以调用另一个方法呢,因为JavaScript 的 Function 实际上是功能完整的对象啊,对象就有属性和方法啊
(1)、call 和 apply 的区别
没啥区别,就接受参数的方式不一样而已,apply 接受的第二个参数是集合,call接受的参数不固定用逗号隔开。
但可以说 call 是包装在 apply 的语法糖,内部实现也是将参数转数组的形式,所以某种意义上讲apply的效率高一点。(2)、call 和 apply 的用途
改变this的指向:
function Obj() { this.name = 'HI 田生~' function fun() { console.log(this.name) // undefined } fun()}new Obj()
上面小节也讲了,fun 没绑定到对象上,所以在这里被当做普通函数使用,this指向全局对象,那咱们要
this.name正常输出怎么办:// 方法一:传统的 _this 传递function Obj() { this.name = 'HI 田生~' var _this = this function fun() { console.log(_this.name) // HI 田生~ } fun()}new Obj()// 方法二:借助 apply 或 callfunction Obj() { this.name = 'HI 田生~' function fun() { console.log(this.name) // HI 田生~ } fun.apply(this)}new Obj()
哪种方法好用我就不多说了
借用其他对象方法
var arr = []Array.prototype.push.apply(arr,[1, 2, 3])console.log(arr) // [1, 2, 3]
三、闭包和高阶函数
1、闭包
闭包这个概论总是不好理解,你可以简单的理解为 闭包就是能够读取其他函数内部变量的函数
var func = function () { var a= 1 return function () { a++ console.log(a) }}var fun = func()fun() // 2fun() // 3fun() // 4
变量 a 是函数 func 的局部变量,外部函数或对象无法访问,但 func 内部的匿名函数能访问,那就这个匿名函数就是一个闭包
,将闭包返回出去,相当于将访问权给了外部环境,外部环境就可以访问一个函数的
2、闭包的作用
上面第一小点顺带讲了闭包能使外部环境访问局部变量,是作用点之一。闭包还可以延续局部变量的寿命
// 方式1var report = function (src) { var img = new Image() img.src = src}report('http://wwww.tiansheng.logo.png')// 方式2var report = (function () { var img return function (src) { img = new Image() img.src = src }})()report('http://wwww.tiansheng.logo.png')
利用方式1有可能图片还没加载完数据就丢失了,因为 report 方法执行完局部变量就销毁了,而方法2
利用闭包的方式延长了变量的寿命3、高阶函数
高阶函数至少需要满足以下条件之一:
- 函数可以作为参数传递
- 函数可以作为返回值输出
我们经常写的带有对调函数就是一个高阶函数
例子:/** * 高阶函数 * @param name * @param callBack */function fun(name, callBack) { // do something ... callBack()}
函数作为返回值输出,其实就是一种闭包的表现
下面一个单例模式的例子:var getSingle = function (fn) { var ret; return function () { return ret || (ret = fn.apply(this, arguments)) }}var getScript = getSingle(function () { // ...})var script1 = getScript()var script2 = getScript()console.log(script1 === script2) // true
小结:
本小结写了 【面向对象的JavaScript】、【this、call 和 apply】、 【闭包和高阶函数】 ,为接下去的JavaScript 设计模式做铺垫