最近发现了一种新的学习方式(或者写笔记的方式),感觉很方便。
在 html 中记录,主要是方便写 js 代码,然后以注释的方式写知识点。
因为会有很多同名函数和变量,所以测试代码文件另写另测试。
这样可以一边记录,一边有针对性的测试。测试不会打断记录,测试中,测试一个注释一个。后面回顾的时候,只看记录的 html 即可。
下面就整理下我读 张容铭 的 《JavaScript 设计模式》的笔记吧,只是第一章的内容。
这本书最大的特点就是从工作实际需求出发吧,不会干讲理论,理论结合实践,也是我喜欢的方式。
《JavaScript 设计模式》阅读笔记之第一章:灵活的js
这章主要是以一个简单的登录验证做需求,不断优化代码实现。
优化路径如下:
全局函数=》局部变量函数=》对象=》类=》原型定义=》链式定义和调用
<script>
//登录的需求
//1.1 定义函数实现,导致新创建3个全局变量
function checkName(){
console.log("验证姓名")
}
function checkEmail(){
console.log("验证邮箱")
}
function checkPassword(){
console.log("验证密码")
}
//1.2 函数的另一种形式
//优化:局部变量代替全局变量(函数也是变量),避免全局同名方法覆盖。
var checkName=function(){
console.log("验证姓名")
}
var checkEmail=function(){
console.log("验证邮箱")
}
var checkPassword=function(){
console.log("验证密码")
}
//1.3用对象收编变量
//减少覆盖或被覆盖的风险,一旦被覆盖,所有功能都会失效,出错现象比较明显,方便错误定位。
var checkObject={
checkName:function(){
console.log("验证姓名")
},
checkEmail:function(){
console.log("验证邮箱")
},
checkPassword:function(){
console.log("验证密码")
}
}
//对象使用
checkObject.checkName()
//1.4对象的另一种形式
//函数也是对象,给对象添加方法。下面这种方式 checkObject 在使用 new 关键字创建新对象时,新创建的对象不能继承 checkName 这些方法。即对象不能复用。
//var newFn=new checkObject()
//newFn.checkName() //newFn.checkName is not a function
var checkObject=function(){}
checkObject.checkName=function(){
console.log("验证姓名")
}
checkObject.checkEmail=function(){
console.log("验证邮箱")
}
checkObject.checkPassword=function(){
console.log("验证密码")
}
//函数使用
checkObject.checkName()
//1.5真假对象
//对象复用 将方法放在一个函数对象中,每次调用返回一个新对象,互不影响。
var checkObject=function(){
return{
checkName:function(val){
console.log("验证姓名")
console.log(val)
},
checkEmail:function(){
console.log("验证邮箱")
},
checkPassword:function(){
console.log("验证密码")
}
}
}
//使用
var a=checkObject()
a.checkName(val);
//1.6类也可以
// ES 5 没有 class 关键字,所以使用函数来模拟类。生成实例的时候,使用new关键字。类的属性和方法,还可以定义在构造函数的prototype对象之上。
//构造函数使用全局作用域
var scope = "global";
function constructFun(){
var scope = "local";
//构造函数可以是匿名的,可以传入任意数量的字符串实参,最后一个实参是函数体。
return new Function("return scope");//无法捕捉局部作用域
}
var rel = constructFun()();
console.log(rel);//global 全局没有定义scope,则返回scope is not defined
// ES 6 中的类,使用class关键字,是原型继承的一种语法糖。
var checkObject=function(){
this.checkName=function(){
console.log("验证姓名")
}
this.checkEmail=function(){
console.log("验证邮箱")
}
this.checkPassword=function(){
console.log("验证密码")
}
}
//使用
//用关键字 new 创建类
var a= new checkObject()
a.checkName();
//1.7 一个检测类
//通过 this 定义函数内部的方法,使用new新创建的对象都会对类上的this上的属性进行复制,所以新创建的对象都有自己的一套方法。
//这么做比较消耗,可以优化,将方法绑定在 checkObject 对象的原型上,这样,新创建的对象所拥有的方法就都是一个了,因为它们都依赖 prototype 原型依次寻找,找到的方法都是同一个。
var checkObject=function(){}
//写法一
checkObject.prototype.checkName=function(){
console.log("验证姓名")
}
checkObject.prototype.checkEmail=function(){
console.log("验证邮箱")
}
checkObject.prototype.checkPassword=function(){
console.log("验证密码")
}
//写法优化
//写法二
checkObject.prototype={
checkName:function(){
console.log("验证姓名")
},
checkEmail:function(){
console.log("验证邮箱")
},
checkPassword:function(){
console.log("验证密码")
}
}
//注意:以上两种方式不能混写
//如下混写:后定义的 checkObject.prototype 会整个覆盖先定义的 checkObject.prototype
var checkObject=function(){}
checkObject.prototype.checkName=function(){
console.log("验证姓名 先定义")
}
checkObject.prototype.checkEmail=function(){
console.log("验证邮箱 先定义")
}
checkObject.prototype={
checkEmail:function(){
console.log("验证邮箱 后定义")
},
checkPassword:function(){
console.log("验证密码")
}
}
//测试
var a =new checkObject();
a.checkName();//a.checkName is not a function
a.checkEmail();//验证邮箱 后定义
a.checkPassword();
//使用
var a =new checkObject();
a.checkName();
a.checkEmail();
a.checkPassword();
//1.8方法还可以这样用
//以上使用方法,调用了3个方法,对象a书写了3遍。
//优化:每个方法的末尾,返回当前对象 this ,这样可以使用链式调用。
//优化对象
var checkObject={
checkName:function(){
console.log("验证姓名")
return this
},
checkEmail:function(){
console.log("验证邮箱")
return this
},
checkPassword:function(){
console.log("验证密码")
return this
}
}
//使用
checkObject.checkName().checkEmail().checkPassword()
//优化类
var checkObject=function(){}
checkObject.prototype={
checkName:function(){
console.log("验证姓名")
return this
},
checkEmail:function(){
console.log("验证邮箱")
return this
},
checkPassword:function(){
console.log("验证密码")
return this
}
}
//使用类 需要先创建一下
var a =new checkObject();
a.checkName().checkEmail().checkPassword();
//1.9函数的祖先
//prototype.js
//扩展原生对象
Function.prototype.checkEmail=function(){
console.log("验证邮箱")
}
//使用
//函数形式
var f=function(){}
f.checkEmail();
//类的形式
var f=new Function()
f.checkEmail();
//这样做会污染原生对象 Function
//优化:抽象出一个统一添加方法的功能
Function.prototype.addMethod=function(name,fn){
this[name]=fn;
}
//使用
var methods=function(){};
//或
var methods=new Function();
methods.addMethod('checkName',function(){console.log("验证姓名")})
methods.addMethod('checkEmail',function(){console.log("验证邮箱")})
methods.checkName();
methods.checkEmail();
//1.10链式添加和调用
//函数式调用
Function.prototype.addMethod=function(name,fn){
//注意
this[name]=fn;
return this;
}
//使用
var methods=function(){};
methods.addMethod('checkName',function(){
console.log("验证姓名")
return this;
}).addMethod('checkEmail',function(){
console.log("验证邮箱")
return this;
})
//函数式调用
methods.checkName().checkEmail();
//1.11换一种方式使用方法
//为了类式调用改造方法
Function.prototype.addMethod=function(name,fn){
//注意
this.prototype[name]=fn;
return this;
}
//使用
var methods=function(){};
methods.addMethod('checkName',function(){
console.log("验证姓名")
return this;
}).addMethod('checkEmail',function(){
console.log("验证邮箱")
return this;
})
//类式调用
var m=new methods();
m.checkName().checkEmail();
//课后作业
//1、真假对象(1.5)中如何实现方法的链式调用
var checkObject=function(){
return{
checkName:function(){
console.log("验证姓名")
return this
},
checkEmail:function(){
console.log("验证邮箱")
return this
},
checkPassword:function(){
console.log("验证密码")
return this
}
}
}
//使用
var a=checkObject()
a.checkName().checkEmail().checkPassword();
//2、定义一个可以为函数添加多个方法的 addMethod 方法
var arr=[
{
name:'checkName',
fn:function(){
console.log("验证姓名")
return this;
}
},
{
name:'checkEmail',
fn:function(){
console.log("验证邮箱")
return this;
}
}
];
Function.prototype.addMethod=function(arr){
arr.forEach(item => {
this[item.name]=item.fn;
});
return this;
}
//使用
var methods=function(){};
methods.addMethod(arr)
//函数式调用
methods.checkName().checkEmail();
//3、定义一个既可以为函数原型添加方法,又可以为其自身添加方法的 addMethod 方法
var arr=[
{
name:'checkName',
isPrototype:true,
fn:function(){
console.log("验证姓名")
return this;
}
},
{
name:'checkEmail',
isPrototype:false,
fn:function(){
console.log("验证邮箱")
return this;
}
}
];
//为了方便测试,引入fn2参数,实际使用去掉即可。
Function.prototype.addMethod=function(arr,fn1,fn2){
arr.forEach(item => {
if(item.isPrototype){
//为函数原型添加方法
this[item.name]=item.fn;
}else{
//为其自身添加方法
fn1[item.name]=item.fn;
}
})
}
//使用
var fn1=function(){};
var fn2=function(){};
fn1.addMethod(arr,fn1,fn2);
fn2.addMethod(arr,fn1,fn2);
//函数式调用
fn1.checkName().checkEmail();
fn2.checkName();
fn2.checkEmail();//fn2.checkEmail is not a function
</script>
关于 this 指向性问题
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁。
推荐阅读
https://www.cnblogs.com/pssp/p/5216085.html
精华摘录
情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window(严格模式中this指向undefined)。
情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,this指向的是最后调用它的对象。
new,call,apply,bind 会改变函数中 this 的指向。
当this碰到return时
如果返回值是一个对象,那么this指向的就是那个返回的对象,否则 this 还是指向函数实例。
还有一点就是虽然 null 也是对象,但是在这里 this 还是指向那个函数的实例。
.prototype 的两种写法
先写 .prototype={},后写 prototype.xxx=function(){} 可以正常使用。
反之被替换报错。
<script>
//测试 .prototype 的两种写法共存
//先写 .prototype={},后写 prototype.getImageLength=function(){} 可以正常使用。反之被替换报错。
//原因:先写对象,后添加对象的属性可以。
var LoopImages=function(imgArr,container){
this.imagesArray=imgArr;
this.container=container;
}
LoopImages.prototype={
createImage:function(){console.log("createImage")},
changeImage:function(){console.log("changeImage")}
}
LoopImages.prototype.getImageLength=function(){
console.log(this.imagesArray.length);
return this.imagesArray.length;
}
//渐隐切换类
var FadeLoopImg=function(imgArr,container,arrow){
//构造函数继承图片轮播类
LoopImages.call(this,imgArr,container);
//切换箭头
this.arrow=arrow;
}
FadeLoopImg.prototype=new LoopImages();
//重写继承的切换方法
FadeLoopImg.prototype.changeImage=function(){
console.log('FadeLoopImg changeImage function')
}
//测试用例
//实例化一个渐隐切换图片类
var fadeImg=new FadeLoopImg(['1.jpg','2.jpg','3.jpg','4.jpg'],'slide',['left.jpg','right.jpg']);
fadeImg.changeImage()//FadeLoopImg changeImage function
fadeImg.createImage()//createImage
fadeImg.getImageLength()//4
</script>