(一) 继承模式
- 传统形式(原型链)
(1) 缺点:过多的继承了没用的属性
(2) 代码展示
//缺点:过多的继承了没用的属性 Grand.prototype.lastName = ‘张‘; function Grand(){ this.name = ‘国庆‘; } var grand = new Grand(); Father.prototype = grand; function Father(){ this.name = ‘改革‘; } var father = new Father(); Son.prototype = father; function Son(){ this.name = ‘小宝‘; } var son = new Son(); //son这里只想用Grand.prototype上的属性,可是这样一继承,不但继承了Grand.prototype上的属性, // 也继承了一些不必要的属性,如father上的属性 |
- 借用构造函数(call/apply)
(1) 缺点:① 只能继承借用函数的方法和属性,不能用他的原型
② 每次构造函数都要多走一个函数
(2) 代码展示
function PersonOne(name,age,sex){ this.name = name; this.age = age; this.sex = sex; } function PersonTwo(name,age,sex,color){ //var this = { __proto__:PersonTwo.prototype } PersonOne.call(this,name,age,sex); this.color = color; } var person1 = new PersonTwo(‘小李‘,23,‘男‘,‘blue‘); console.log(person1); |
- 共享原型
(1) 缺点:不能随便改变自己的原型,因为自己的原型和原来的函数用的是 一个原型,自己的改,原来的相应的也会去改
(2) 代码展示:
//共享原型封装 function inherit(Target,Origin){ Target.prototype = Origin.prototype; } //应用 Father.prototype = { name:‘小张‘, age:29 } function Father(){ } function Son(){ } inherit(Son,Father); var son = new Son(); console.log(son.name); //小张 //缺点:不能随便改变自己的原型,因为自己的原型和原来的函数用的是 //一个原型,自己的改,原来的相应的也会去改 var father = new Father(); Son.prototype.name = ‘小红‘; console.log(son.name); //小红 console.log(father.name); //小红 |
(3) 代码图解:
- 圣杯模式(解决共享原型的不足)(用中间的构造函数解决)(最完美的继承模式)
(1)圣杯模式1
① 代码
//为了解决共享原型模式的不足,借用第三方函数,构建了圣杯模式 //封装 function inherit(Target,Origin){ function F(){} //① F.prototype = Origin.prototype; //② Target.prototype = new F(); //③ } //上面的代码中的②和③是不能调换位置的,因为这样的话Target会在继承Origin //原型之前,从而先继承了F的原型 //应用 Father.prototype = { name:‘小张‘, age:29 } function Father(){ } function Son(){ } inherit(Son,Father); var son = new Son(); console.log(son.name); //小张 var father = new Father(); Son.prototype.name = ‘小红‘; console.log(son.name); //小红 console.log(father.name); //小张 |
② 图解代码
(2) 圣杯模式2(因为圣杯模式1中继承关系,son的构造函数指向了Father,为了解决这一问题,推出了圣杯模式2)
① 说明son的构造函数为什么指向了Father
② 解决方法代码展示
//封装 function inherit(Target,Origin){ function F(){} //① F.prototype = Origin.prototype; //② Target.prototype = new F(); //③ Target.prototype.constructor = Target; //④ 让Target.prototype指向的构造函数从Origin处转回到Target处 Target.prototype.uber = Origin.prototype; //⑤ 让构造出来的对象找到自己的超类(超级父级)(就是它最终继承于谁) } |
(3) 圣杯模式3(建议使用)(雅虎的工程师利用闭包的第三个特点:可以实现封装,属性私有化;推出了圣杯模式3)
var inherit = (function (){ var F = function (){}; return function(Target,Origin){ F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target; Target.prototype.uber = Origin.prototype; } }()) |
- 闭包的第三个特点的应用(可以实现封装,属性私有化)
(1)代码展示
//可以实现封装,属性私有化 function Deng(name,wife){ var preperewife = ‘xiaozhang‘; this.name = name; this.wife = wife; this.divorce = function(){ //① this.wife = preperewife; // 这个对象里面的属性为什么可以用外部的变量呢 }, this.changePreparewife = function(target){ //② preperewife = target; }, this.sayPreparewife = function(){ //③ console.log(preperewife); } } var deng = new Deng(‘deng‘,‘xiaoliu‘); //①②③函数和Deng函数形成了闭包 |
(2)代码说明
//deng中只保存了Deng函数中的那个this对象,因为最后有个return this,形成闭包后,把这个 //preperewife这个属性拿走了,形成了自己的私有化变量 //为什么通过deng.preperewife访问属性时,返回undefined,因为this对象上根本没有这个属性 //只是this上的①②③方法通过AO拿到了这个变量(preperewife),所以通过this上相应的方法可以 //访问到它,这个preperewife就好比自己的私人变量,从表面上直接是访问不到的 |
//而雅虎工程师正是利用了闭包的这一特点,对圣杯模式进行了改进,才成为了下面的圣杯模式 // var inherit = (function (){ // var F = function (){}; // return function(Target,Origin){ // F.prototype = Origin.prototype; // Target.prototype = new F(); // Target.prototype.constructor = Target; // Target.prototype.uber = Origin.prototype; // } // }()) |
(二) 命名空间
- 概念:管理变量,防止污染全局,适用于模块化开发
- 在企业开发中,每个人会负责某一项目上的某一功能,最后把这些功能合并在一起这样肯定会在一个html文件中引入多个js文件,这样的话,每个js文件中可能有
变量名的冲突,出现变量名覆盖的现象,从而想出了命名空间
- 代码说明
//在企业开发中,每个人会负责某一项目上的某一功能,最后把这些功能合并在一起 //这样肯定会在一个html文件中引入多个js文件,这样的话,每个js文件中可能有 //变量名的冲突,出现变量名覆盖的现象,从而想出了命名空间 var obj = { department1:{ zhangsan:{ name:‘xiaolizi‘ }, lisi:{ name:‘xiaowangba‘ } }, department2:{ wangmazi:{ name:‘xiaokeai‘ } } } //调用 obj.department1.zhangsan.name; //但是这样写太麻烦,可以改进一下 var zhangsan = obj.department1.zhangsan; zhangsan.name; //但是命名空间现在因为各种工具的出现(如:webpack)已经被弃用了 //虽然命名空间这种方法已经不用了,但是我们依然可以在不使用工具的情况下 //解决上面的问题 |
- 上面所说的在不使用工具的情况下解决问题的方法就是利用闭包的第四个特点
- 闭包的第四个特点的应用(模块化开发,防止污染全局变量)
(1) 代码展示与说明
//虽然命名空间这种方法已经不用了,但是我们依然可以在不使用工具的情况下 //解决上面的问题 //这种方法就是闭包+立即执行函数,这也正是闭包的第四点应用:模块化开发, //防止污染全局变量 var name = ‘bcd‘; var init = (function(){ var name = ‘abc‘; function callName(){ console.log(name); } return function(){ callName(); } }()) init(); |
- init是初始化的意思,企业开发时,我们经常把入口函数用init命名
- 原生js模拟jquery链式调用
//在一个对象里面,this指向第一人称,即这个对象 var deng = { smoke : function() { console.Log(‘ Smoking,... xuan cool!!!‘); return this; }, drink: function () { console.log(‘drinking..., ye cool!‘); return this; }, perm: function () { console.Log(‘ preming.... cool!‘); return this; } } deng.smoke().drink().perm().smoke().drink(); |
- 在一个对象里面,this指向第一人称,即这个对象
- 属性的表示方法
(1)obj.prop
(2)obj[‘prop’](这个比较灵活,建议使用这个)
(3)obj.prop ——> obj[‘prop’](这是隐式转换过程)
(4)属性名的拼接必须使用中括号
(5)代码展示
var deng = { wife1: { name: "xiaoliu" }, wife2: { name: "xiaozhang" }, wife3: { name: "xiaomeng" }, wife4: { name: "xiaowang" }, sayWife: function (num) { return this[‘wife‘ + num]; } } // 隐式转换 //obj.属性名 ————————————> obj[‘属性名‘] |
(一) 对象的枚举
- 对象中属性是字符串形式的
- for in 循环
(1) 在开发中,对象中的length我们是无从知道的,对象中放的什么我们也不知道,但是我们现在想知道里面有什么,就必须用到for in循环
(2) for in 循环的主要目的就是遍历对象,它是通过对象中属性的个数来控制循环圈数的
(3) 代码展示与说明
// var obj = { // name:‘xiaolizi‘, // age:23, // sex:‘男‘ // } // for( var i in obj ){ // console.log(obj.i); //undefined // } //为什么会打印出undefined,因为 //obj.i ————> obj[‘i‘] ————> 它认为这个i是一个字符串(属性) //解决方案 var obj = { name:‘xiaolizi‘, age:23, sex:‘男‘ } for( var i in obj ){ console.log(obj[i]); } |
- hasOwnProperty
(1) 因为for in循环会把我们自己手动给原型上添加的属性,打印出来
这是我们不需要的,因此我们需要hasOwnProperty进行过滤
(2) 代码展示与说明
//hasOwnProperty 可以判断这个属性属于原型还是属于自己 var obj = { name:‘xiaolizi‘, age:23, sex:‘男‘, __proto__:{ lastName:‘zhang‘ } } for( var i in obj ){ if( obj.hasOwnProperty(i) ){//判断是i(自己)的属性还是原型上的属性 console.log(obj[i]); } } |
(3) for in循环会打印自己手动设置的原型方法和属性,但是不会打印系统设定好的方法和属性
- in 操作符
(1) ‘属性名’ in 对象 (该属性属不属于这个对象)(true/false)
(2) in无法分别这个属性是属于对象自己的还是原型的
- instanceof
(1) A instanceof B
① A对象是不是B构造函数构造出来的对象(官方)
② 看A对象的原型链上有没有B的原型
(2) {}.constructor 不能这样写,因为可能把{}识别为函数体
- 区别一个变量是数组还是对象的方法
(1) 通过构造器 变量.constructor
(2) 通过instanceof 变量 instanceof Array
(3) toString() (推荐)(因为前两个在父子域问题上有一点差错)
① 各个构造函数都修改了toString()
② Object.prototype.toString.call(变量)
③ Object.prototype.toString = function() {}
④ 谁调用toString(),this就指向谁,③的式子中this指向终极原型
⑤ 用call可以改变this的指向,从而让this指向②中的这个变量
原文地址:https://www.cnblogs.com/080-hll/p/12506218.html