继承
1.传统模式--- >原型链 过多的继承了没用的属性。
2.借用构造函数 不能继承借用构造函数的原型。 每次构造函数都要多走一个函数。
3.共享原型 不能随便修改自己的原型
4.圣杯模式
不只是构造函数才有原型,只要是函数都有原型。原型是在构造函数之上的对象
# 传统模式
Grand.prototype.lastName = "ji";
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();
//son能继承自己原型属性,father原型的属性,grand原型的属性
//这样从头继承到尾就会发生一个矛盾,不想继承来的都继承了。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 借用构造函数
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,grade){
Person.call(this,name,age,sex);
this.grade = grade;
}
var student = new Student();
//用call将Person自身的属性全部传到Student里面
//Student就可以完全借用Person的属性和方法。
//一定要写构造函数,要不然此类写法无意义。
2
3
4
5
6
7
8
9
10
11
12
13
# 共享原型
function.prototype.lastName = "Deng";
function Father() {
//son.prototype = Father
}
function Son() {
}
var son = new Son();
var father = new Father();
//想让son生产出的对象 继承自Father的prototype
//让son的原型等于father的原型
Son.prototype = Father.prototype
2
3
4
5
6
7
8
9
10
11
# 共享原型弊端
function.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
function inherit(Target,Origin) {
Target.prototype = Origin.prototype;
}
inherit(Son,Father);
var son = new Son();
var father = new Father();
// inherit(Son,Father);写到这里,那son和 father 的原型都为 undefined
2
3
4
5
6
7
8
9
10
11
12
弊端
封装一个函数inherit里面传入的参数是继承人,继承者。
此类写法的弊端是son如果有一天想要一个别的属性,Son.prototype.sex = "male",那么Father就也会有father.sex。
# 圣杯模式
# 基本写法
function F() {}
F.prototype = Father.prototype
Son.prototype = new F()
2
3
# 原理
通过原型链来实现继承
F 的 prototype 就是 Father 的 prototype
Son的 prototype 等于构造函数 F
Son 的 prototype 等于 Father 的 prototype
改变son的属性和方法不会改变Father的属性和方法
# 完整圣杯模式
function inherit(Target,Origin) {
function F(){ };
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constuctor = Target;
//son的constuctor让他指向他自己
Target.prototype.uber = Origin.prototype;
//son的超类(真正继承自father的prototype)
}
function.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
inherit(Son,Father);
var son = new son();
var father = new Father();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
此刻son.lastName=deng,father.lastName=deng
给Son加上.prototype.sex = "male" son.sex. = sex。 fater.sex = undefined。
原型上的construtor默认指向它的构造函数,son的constructor理应指向Son,现在就成为了father。 son.proto -- > new F().proto -- >Father.prototype
# 命名空间
# 作用 管理变量,防止污染全局,适用于模块化开发
var org = {
department1 : {
jicheng : {
name : "abc";
age : 123;
}
xuming : {
}
},
department2 : {
zhangsan : {
},
lisi : {
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
把属性都写到对象里面,让变量命名不冲突。 使用的话 可以这样写
org.department1.jicheng.name//调用
var jicheng = org.department1.jicheng;//简化写法
jicheng.name,jicheng.age //调用
2
3
# 闭包私有化功能
闭包的第三四个功能
闭包可以实现封装,属性私有化功能。
管理变量 防止污染全局,适用于模块化开发。
var name = 'bcd';
var init = (function () {
var name = 'abc';
function callName() {
console.log(name);
}
return function () {
callName();
}
}());
init();
//写一个返回的接口函数,调用里面的其他方法
//当整体大函数运行的时候,会运行此函数
//打印 abc,不会污染全局变量name的bcd,实现私有化变量
var initdeng = (function () {
var name = 'laodeng';
function callName() {
console.log(name);
}
return function () {
callName();
}
}());
initdeng() //打印 laodeng
//两个立即执行函数就实现了自己功能的封装,也没有污染全局变量
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 模拟jq连续调用
var deng = {
smoke: function () {
console.log('Smoking,... xuan cool!');
// return undefined;
return this;
},
drink: function () {
console.log('drinking...,ye cool!');
return this;
},
perm: function () {
console.log('perming...,cool!');
return this;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
deng.smoke().drink()
会报错,因为smoke方法调用之后somke下面会隐式的返回undefined
,undefined.drink()
一定报错。
如果想实现一个方法的连续调用, 给方法后面写一个return this
,相当于deng.下一个方法
,就可以完成连续调用。
# 属性的表示方法
# 两种表示方法
1.obj.prop
2.obj["prop"]
var obj = {
name: "abc";
}
//想要访问name 有两种方式。第一种obj.name
//第二种obj['name']
//obj后面跟一个[] 里面写字符串的属性名
2
3
4
5
6
# obj['name']可以延伸以下写法
例子(属性拼接)
var deng = {
wife1: { name: "xiaoliu" },
wifi2: { name: "xiaozhang" },
wife3: { name: "xiaomeng" },
sayWife: function (num) {
return this['wife' + num];
}
}
2
3
4
5
6
7
8
原理
return this
指第一人称自己,deng.[wife
+传进来的数字
]。
因为字符串可以进行拼接成另一个字符串
,使用传参方式输出。
deng.sayWife(1);打印xiaoliu,以此类推
# 对象枚举
# 数组遍历
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// 遍历 枚举
for (var i < 0; i < arr.length; i++) {
console.log(arr[i]);
}
2
3
4
5
# 对象遍历
已知一个对象,遍历里面的属性和属性值。
var obj = {
name: '13',
age: 123,
sex: "male",
height: 180,
weight: 75
}
for (var prop in obj) {
//console.log(prop)打印所有属性名
console.log(obj.prop)
// var prop的prop可以自定义改成其他的单词
//但是打印必须是 obj.自定义单词
}
2
3
4
5
6
7
8
9
10
11
12
13
# for in 循环
遍历对象功能。对象有多少个属性,就会循环多少圈。
上面代码会打印出5个undefined
,因为输出obj.prop系统隐式转换obj['prop']
,结果obj里面没有['prop']
,所以会打印undefined。
console.log(obj[prop])
只有这么写才会把对象里面的属性值遍历出来。
一般情况下用到 for in 循环,会写 hasOwnProperty,判断属性属不属于对象,不会取到原型链上的属性。(后面章节深度克隆会写到)。
# hasOwnProperty
任何对象都有hasOwnProperty这个属性。
for (var prop in obj) {
if(obj.hasOwnProperty(prop)) {
console.log(obj.[prop]);
}
}
2
3
4
5
写法:obj.hansOwnProperty( )
,里面传属性判断
。但凡是自己手动写的属性都可以判断出来在对象里,也都能打印。如果不是自己手动设置的,是原型链上的,不会打印系统自带的属性。
功能:判断属性属不属于对象
,返回布尔值。
# in
功能:只能判断对象上能不能访问属性,包括原型。
'lastname' in obj //true
//属性名要加字符串,要不然会当成变量 lastname 未定义
//in 和 hasOwnProperty相似,区别就是in 能访问原型,hanOwnProperty 不能
2
3
# instanceof
function Person() {
}
var person = new Person();
person instanceof Person //打印true
person instanceof Object //打印true
[] instanceof Array //打印true
[] instanceof Object //打印true
2
3
4
5
6
7
8
9
``
A instanceof B
A对象 是不是 B构造函数构造出来的。
A对象的原型链上 有没有B的原型。
# 区分 Array Object
var obj = { };
var arr = [ ];
2
第一种区别方式(construtor)
第二种区别方式(instanceof)
第三种区别方式(toString)
Object.prototype.toString = function () {
//识别 this
//返回相应的结果
//this = obj;谁调用的方法 this 就指向谁
}
obj.toString();
Object.prototype.toString.call([]);
//用 call 把数组传进去此时 this 指向数组
// 从而识别数组,对象等
2
3
4
5
6
7
8
9
10