继承模式、命名空间、对象枚举
文章目录
继承发展史
1.传统形式—->原型链
过多的继承了没用的属性
// son把原型链上的所有东西都继承下来的 不需要的都继承了
Gand.prototype.lastName = 'zhang'
function Grand() {}
var grand = new Grand();
Father.prototype = grand;
function Father() {
this.name = 'hehe'
}
var father = new Father();
Son.prototype = father;
function Son() {}
var son = new Son();
⒉.借用构造函数
不能继承借用构造函数的原型(不算继承)
// 借用他人方法实现自己的功能 只适用于他人属性都是我需要的前提下
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Students(name, age, sex, grade, tel) {
Person.call(this, name, age, sex);
// Person.apply (this, [name, age, sex]); 两种方法都可以
this.grade = grade;
this.tel = tel;
}
var student = new Student();
每次构造函数都要多走一个函数(浪费效率)
// 每次构造函数都需要执行Student和Person这两个函数
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, grade, tel) {
Person.call(this, name, age, sex);
// Person.apply (this, [name, age, sex]); 两种方法都可以
this.grade = grade;
this.tel = tel;
}
var student = new Student();
3.共享原型(公有原型)
共享原型
// 不通过原型链继承原型的方法(公有原型)
Father.prototype.lastName = 'cheng'
function Father() {}
function Son() {}
Son.prototype = Father.prototype // prototype是对象 可直接赋值
var son = new Son(); // 访问father.lastName输出cheng
var father = new Father(); // 访问son.lastName输出cheng
// 说明father和son都继承了Father的原型
继承
继承的两种写法
1. function extend()
2. function inherit() // inherit继承 也是css属性的一个值(如果子元素没设置文字属性的话,默认继承父元素的文字属性)
// font-size : inhert // 所以子元素如果没有属性就默认继承父元素的
Father.prototype.lastName = 'cheng'
function Father() {}
function Son() {}
// origin是原始的 这是让target继承origin 传进来的是一个构造函数 因为只要是构造函数继承了 那生产的所有对象就全继承了
function inherit(Target, Origin) { // 这是定义inherit的方法
Target.prototype = Origin.prototype;
}
inherit(Son, Father); // 两个构造函数共用原型 Son继承Father的
var son = new Son();
// inherit(Son, Father); 继承写这里就晚了 都已经构造完对象了 还是继承的之前的原型
不能随便改动自己的原型
// inherit继承的不足 改动的原型是共用的 一个改全部改
Father.prototype.lastName = 'cheng'
function Father() {}
function Son() {}
function inherit(Target, Origin) {
Target.prototype = Origin.prototype;
}
inherit(Son, Father);
Son.prototype.sex = 'male'; // 给原型增加sex属性
var son = new Son(); // 访问son.sex输出male
var father = new Father(); // 访问father.sex输出male
// Son.prototype和Father.prototype指向的是一个原型 一改全改
4.圣杯模式
// F.prototype做一个中间层 继承Father的原型 Son又继承F的原型 原型只能更改自己的 所以不影响Son自己增加属性 相当于小型原型链 这里是原型 不是继承 所以不会影响(继承是共同指向一个位置 原型是copy)
function F() {}
F.prototype = Father.prototype;
Son.prototype = new F();
提取||
||公式
//圣杯模式
function inherit(Target, Origin) { // 这里定义方法
function F() {}
//下面两行不能调换 要先更改原型再构造对象 不能构造对象之后再换原型,如果调换了,new F()的原型就不是Origin的原型
Target.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target; // 这是为了让构造器归位 比如son访问构造器 就是它自己
Target.propotype.uber = Origin.prototype; // 可有可无 可以知道它真正继承自谁
}
Father.prototype.lastName = 'cheng'
function Father() {}
function Son() {}
inherit(Son, Father);
var son = new Son();
var father = new Father();
Son.prototype.sex = 'male'; // 给Son增加sex属性
son.sex; // 输出male 说明son的原型增加了sex属性
father.sex; // 输出undefined 说明了两者互不影响
// constructor是原型的属性,而F是对象,没有这个属性,所以找F的原型 Father,prototype Father,prototype上的constructor指向Father
// son.__proto__ --> new F().__proto__ --> Father.prototype
son.constructor; // 输出 function Father() {}
// 私有化变量(上面代码的更好的写法)
var inherit = (function () {
var F = function () {};
return function demo (Target, 0rigin) {
F.prototype = 0rigin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = 0rigin.prototype;
}
return demo;
}());
// 为啥形成闭包 因为隐式三步里最后一步是return this 然后函数保存在this里,最后跟踪this一起被保存到了外部
//私有化变量 通过Deng.preparewife访问结果是undefined
function Deng( name, wife) {
var preparewife = "xiaozhang";
this. name = name;
this.wife = wife;
this.divorce = function () {
this.wife = preparewife;
}
this.changePreparewife = function (target){
preparewife = target;
}
this.sayPraprewife = function ( ) {
console.log(preparewife);
}
}
var deng = new Deng( 'deng' , 'xiaoliu' );
命名空间
管理变量
// 防止变量名重复 把所有的变量名都放在一个全局对象中
// 现在不这么用了 了解一下就好
var org = {
department1 : {
jicheng : {
name : "abc" ,
age : 123
},
xuming : {
}
},
departmeng2 : {
zhangsan : {
},
lisi : {
}
}
}
// 引用变量
var jicheng = org.department1. jicheng;
jicheng.name
防止污染全局、适用于模块化开发
// 闭包拥有独立词法作用域,不会污染全局作用域
var init = ( function () {
var name = 'abc ';
function callName( ) {
console.log( name) ;
}
return function () {
callName( );
}
}())
init(); // 执行init(初始化的意思)就相当于调用函数里面的东西
// 这个函数里面的变量名和上面的互不影响 其实就是局部变量
var initDeng = (function ( ) {
var name = 123;
function callName( ) {
console.log( name) ;
}
return function () i
callName( );
}
}())
问题
如何实现链式调用模式(模仿jQuery)
<!--jQuery调用css方法-->
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="jQuery.js"></script>
</ head>
<body>
<div></div>
<script type="text/javascript">
$( 'div').css('background-color','red').width(100).height(100).html(123).css('position','absolute' ).css( 'left', '100px').css( 'top','100px');
</script>
</body>
obj.eat().smoke().drink().eat().sleep();
<!--链式调用-->
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();这样写会报错 因为deng.smoke()没有返回值 所以有个隐式的返回undefined 第二个方法就相当于undefined.drink() 这会报错 所以想每个函数链式调用 就需要每个函数之后都返回this(函数本身)
deng.smoke().drink().prem() // 返回this之后就可以链式调用
属性表示方法
obj.prop
var obj = {
name : "abc"
}
obj.name ----> obj ['name'] // obj隐式转换过程 所以obj.name和obj['name']都可以访问到name这个变量 注意第二种访问方式是字符串 要加引号 第二种方式更灵活 因为字符串可以拼接
obj[“prop”]
// obj.prop的使用 实现属性名的拼接
var deng = {
wife1 : {name : "xiaoliu"},
wife2 : {name : "xiaozhang"},
wife3 : {name : "xiaomeng"},
wife4 : {name : "xiaowang"},
saywife : function (num){
return this [ 'wife' +num]; // 属性名拆分成wife+num
}
}
对象枚举
.// 遍历数组
var arr =[1,3,3,4,5,6,7,8,9];
// 遍历 = 枚举 = enumeration
for(var i = 0; i < arr.length; i++){
console.log(arr[i]);
}
for in
var obj = {
name : '13',
age : 123,
sex : "male" ,
height : 180,
weight : 75
}
// prop in obj 就是遍历obj中所有的属性 有多少个属性就遍历多少次 在每次循环的时候 会把属性名放在prop这个对象里面 prop也可以自定义别的名字 换成key也行 prop相当于i
for(var prop in obj) {
// console.log(prop + "" + typeof(prop) );
// console.log(obj.prop); 这么写打印undefined 因为这样是在obj中找属性名是prop的属性值进行打印 然而没这个属性 所以打印undefined
console.log(obj.[prop]); // 加[]之后prop才代表不同的变量 每次都把不同的属性放到prop里面 然后打印属性值 切记不要加引号
}
var obj1 = {
a : 123,
b : 234,
c : 345
}
var key;
for(key in obj1) {
obj1.key ++;
}
var obj = {
name : '13',
age : 123,
sex : "male",
height : 180,
weight : 75,
__proto__: {
lastName : "deng"
}
}
for(var prop in obj) {
// 这里打印会把__proto__(原型)也打印出来
console.log(obj[prop] );
}
hasOwnProperty
判断是不是自身的属性。
var obj = {
name : '13',
age : 123,
sex : "male",
height : 180,
weight : 75,
__proto__: {
lastName : "deng",
}
}
for(var prop in obj) {
// 判断是不是自身的属性 是就打印 不是就不打印 __proto__是原型 所以不会打印
if(obj.hasOwnProperty(prop)) {
console.log(obj[prop]);
}
}
in
in判断的是这个对象中能不能访问到这个属性,所以原型中的属性也算它的。
'height' in obj ==> true
'lastName' in obj ==> true
instanceof
function Person( ) {}
var person = new Person( );
var obj = {};
// 官方:A对象是不是B构造函数构造出来的
// 成哥:看A对象的原型链上有没有B的原型
//A instanceof B
person instanceof Person --> true
person instanceof Object --> true // 原型链顶端有Object
obj instanceof Person --> false // 这俩没关系
数组和对象的类型都是Object
// 判断数据的正确类型
var arr = [] || {};
var obj = {};
方法一:判断数组和对象的构造器
数组
对象
方法二: instanceof
方法三:toString方法
// 调用this方法就是返回对应的类型,比如number调用toString返回[object,Number],对象数组返回结果与之类似
0bject.prototype.toString.call([]); // 用call来执行 把this变成数组 所以数组就会替换this的位置 就会识别数组来返回结果
0bject.prototype.toString = function ( ) {
// 这个this 谁调用的就指向谁
// 识别this
// 返回相应的结果
}
obj.toString();
根据返回的结果就能知道数据类型
295)]
对象
[外链图片转存中…(img-hUbHzVvO-1664961393296)]
方法二: instanceof
[外链图片转存中…(img-Z1IsvpV3-1664961393296)]
方法三:toString方法
// 调用this方法就是返回对应的类型,比如number调用toString返回[object,Number],对象数组返回结果与之类似
0bject.prototype.toString.call([]); // 用call来执行 把this变成数组 所以数组就会替换this的位置 就会识别数组来返回结果
0bject.prototype.toString = function ( ) {
// 这个this 谁调用的就指向谁
// 识别this
// 返回相应的结果
}
obj.toString();
根据返回的结果就能知道数据类型