1.面向对象的概念
// 思想的进化之路:
2. 面向对象编程:
- 简单方式创建一个对象和工厂模式批量创建对象
示例代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>工厂创建对象的方式</title> </head> <body> <script> // 1.创建对象方式1 var a = {}; // 缺点就是不能重复利用 设置公共属性的代码 // 2.创建对象方式2 工厂模式 // 优点:可以进行批量的创建 // 缺点: ① 对象的方法不能和其他对象共享,多占用内存 // ② 不能识别对象的原型和构造函数 // 故:后续的代码中 c instanceOf Cat; // false function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。 var o = new Object(); // 创建一个对象 // 为这个对象添加属性 o.age = age; o.name = name; o.run = function(){ console.log(o.name + " is running!"); } return o; } var c = createCat(19, 'lili'); // 创建一只叫lili的猫 c.run(); // lili is running! </script> </body> </html>
示例讲解图:
-
构造函数创建对象
示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>面向对象编程</title> </head> <body> <script> // 1.创建对象方式1 var a = {}; // 缺点就是不能重复利用 设置公共属性的代码 // 2.创建对象方式2 工厂模式 // 优点:可以进行批量的创建 // 缺点: ① 对象的方法不能和其他对象共享,多占用内存 // ② 不能识别对象的原型和构造函数 // 故:后续的代码中 c instanceOf Cat; // false function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。 var o = new Object(); // 创建一个对象 // 为这个对象添加属性 o.age = age; o.name = name; o.run = function(){ console.log(o.name + " is running!"); } return o; } var c = createCat(19, 'lili'); // 创建一只叫lili的猫 c.run(); // lili is running! // 3.创建对象方式3 构造函数创建对象的模式 // 优点: // ① 创建对象的时候默认初始化一些属性。 // ② 可以使用instanceOf追溯对象的原型及构造函数 // 缺点:对象的方法不能重用,每个对象都会存储一份,造成内存浪费。 // 当使用new来调用构造函数时: // ① 创建一个空对象 // ② 把空对象赋值给this // ③ 执行构造函数内部代码,并给this的属性做赋值初始化 // ④ 把创建的对象返回 function Cat(age, name){ this.age = age; this.name = name; this.run = function(){ console.log(this.name + " is running!!"); }; } var c2 = new Cat(18, "golr"); // 通过构造函数创建一个对象 c2.age = 17; // 修改对象的属性值 c2.run(); // golr is running!! // c2 instanceof Cat // true // c2.constructor === Cat // 因为c1的__proto__指向Cat原型,Cat原型(Cat.prototype)上有constructor </script> </body> </html>
-
原型构造对象的方法
示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>面向对象编程</title> </head> <body> <script> // 1.创建对象方式1 var a = {}; // 缺点就是不能重复利用 设置公共属性的代码 // 2.创建对象方式2 工厂模式 // 优点:可以进行批量的创建 // 缺点: ① 对象的方法不能和其他对象共享,多占用内存 // ② 不能识别对象的原型和构造函数 // 故:后续的代码中 c instanceOf Cat; // false function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。 var o = new Object(); // 创建一个对象 // 为这个对象添加属性 o.age = age; o.name = name; o.run = function(){ console.log(o.name + " is running!"); } return o; } var c = createCat(19, 'lili'); // 创建一只叫lili的猫 c.run(); // lili is running! // 3.创建对象方式3 构造函数创建对象的模式 // 优点: // ① 创建对象的时候默认初始化一些属性。 // ② 可以使用instanceOf追溯对象的原型及构造函数 // 缺点:对象的方法不能重用,每个对象都会存储一份,造成内存浪费。 // 当使用new来调用构造函数时: // ① 创建一个空对象 // ② 把空对象赋值给this // ③ 执行构造函数内部代码,并给this的属性做赋值初始化 // ④ 把创建的对象返回 function Cat(age, name){ this.age = age; this.name = name; this.run = function(){ console.log(this.name + " is running!!"); }; } var c2 = new Cat(18, "golr"); // 通过构造函数创建一个对象 c2.age = 17; // 修改对象的属性值 c2.run(); // golr is running!! // c2 instanceof Cat // true // c2.constructor === Cat // 因为c1的__proto__指向Cat原型,Cat原型(Cat.prototype)上有constructor // 4.创建对象方式4 原型构造对象的模式 function Cat3(){ this.age = 12; this.name = 'serdy'; } // 需要共享的方法或属性,可以在原型上定义 Cat3.prototype.run = function(){ // 所有实例对象都共享的方法 console.log(this.name + " is running..."); }; Cat3.prototype.type = "Orange"; // 所有实例对象都共享的属性 var c3 = new Cat3(); c3.run(); // serdy is running... var c3_2 = new Cat3(); c3_2.run(); // serdy is running... console.log(c3.run === c3_2.run); // true c3.type = "Persian"; // 对象的属性分为: 读取和设置两种模式 // 读取:自己没有就往上找,直到找到,若找不到则为undefined。 // 写入:自己没有,就直接添加 console.log(c3.type); // Persian console.log(c3_2.type); // Orange </script> </body> </html>
运行结果:
-
组合模式(目前比较好(本笔记基于ES3),js在ES6后有了进一步发展,有了class关键字)
示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>面向对象编程</title> </head> <body> <script> // 1.创建对象方式1 var a = {}; // 缺点就是不能重复利用 设置公共属性的代码 // 2.创建对象方式2 工厂模式 // 优点:可以进行批量的创建 // 缺点: ① 对象的方法不能和其他对象共享,多占用内存 // ② 不能识别对象的原型和构造函数 // 故:后续的代码中 c instanceOf Cat; // false function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。 var o = new Object(); // 创建一个对象 // 为这个对象添加属性 o.age = age; o.name = name; o.run = function(){ console.log(o.name + " is running!"); } return o; } var c = createCat(19, 'lili'); // 创建一只叫lili的猫 c.run(); // lili is running! // 3.创建对象方式3 构造函数创建对象的模式 // 优点: // ① 创建对象的时候默认初始化一些属性。 // ② 可以使用instanceOf追溯对象的原型及构造函数 // 缺点:对象的方法不能重用,每个对象都会存储一份,造成内存浪费。 // 当使用new来调用构造函数时: // ① 创建一个空对象 // ② 把空对象赋值给this // ③ 执行构造函数内部代码,并给this的属性做赋值初始化 // ④ 把创建的对象返回 function Cat(age, name){ this.age = age; this.name = name; this.run = function(){ console.log(this.name + " is running!!"); }; } var c2 = new Cat(18, "golr"); // 通过构造函数创建一个对象 c2.age = 17; // 修改对象的属性值 c2.run(); // golr is running!! // c2 instanceof Cat // true // c2.constructor === Cat // 因为c1的__proto__指向Cat原型,Cat原型(Cat.prototype)上有constructor // 4.创建对象方式4 原型构造对象的模式 function Cat3(){ this.age = 12; this.name = 'serdy'; } // 需要共享的方法或属性,可以在原型上定义 Cat3.prototype.run = function(){ // 所有实例对象都共享的方法 console.log(this.name + " is running..."); }; Cat3.prototype.type = "Orange"; // 所有实例对象都共享的属性 var c3 = new Cat3(); c3.run(); // serdy is running... var c3_2 = new Cat3(); c3_2.run(); // serdy is running... console.log(c3.run === c3_2.run); // true c3.type = "Persian"; // 对象的属性分为: 读取和设置两种模式 // 读取:自己没有就往上找,直到找到,若找不到则为undefined。 // 写入:自己没有,就直接添加 console.log(c3.type); // Persian console.log(c3_2.type); // Orange // 5.创建对象方式5 组合模式 function Cat4(age, name){ // 每个对象都有自己私有的属性值的属性,放到构造函数中。 this.age = age; this.name = name; } // 一般‘类型’(在ES6中就进一步有了class关键字,此为后话)都放在原型上,让所有对象都共享方法的内存。 Cat4.prototype.run = function(){ console.log(this.name + " is running!"); }; var c4_1 = new Cat4(12, 'weru'); var c4_2 = new Cat4(10, "oity"); c4_1.run(); // weru is running! c4_2.run(); // oity is running! console.log(c4_1.run === c4_2.run); // true </script> </body> </html>
-
稳妥构造函数模式 (其实和工厂模式很像,不过用这种方式创建实例对象时,兼容了new)
示例代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>面向对象编程</title> </head> <body> <script> // 6.创建对象的方式6 稳妥构造函数模式 (其实和工厂模式很像,不过用这种方式创建实例对象时,兼容了new) function Cat5(){ var cat = {}; // 此处和工厂模式不一样! cat.age = 9; cat.name = 'cty'; cat.run = function(){ console.log(cat.name + " is running.....!"); }; return cat; // 如果是构造函数执行模式,如果返回的是一个引用类型,就把引用类型返回;如果返回的是简单类型,那么就返回this。 } // 稳妥构造函数模式,要实现使用new构造一个对象和不使用new构造一个对象,效果一样。 var c5_1 = new Cat5(); // 构造函数调用模式 var c5_2 = Cat5(); // 函数调用模式 c5_1.run(); // cty is running.....! c5_2.run(); // cty is running.....! // 缺点:不能溯源 原型、构造函数; 对象的方法内存不能共享,浪费内存。 </script> </body> </html>
3.对象的继承(重点)
// 思想的进化历程
1.原型继承模式
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>对象的继承</title>
</head>
<body>
<script>
// 1.原型继承模式
function Animal(age, name){ // 动物基类
this.age = age;
this.name = name;
}
Animal.prototype.run = function(){ // 在动物基类的原型上添加方法run
console.log(this.name + " is running!");
};
function Cat(age, name){
this.age = age;
this.name = name;
}
Cat.prototype = new Animal(); // 原型的继承方式,由于Animal的constructor指向的是Animal构造函数,
// 故,这里还要改回来 !
Cat.prototype.constructor = Cat;
var c = new Cat(12, 'iser');
c.run(); // 从Animal原型上继承的方法
// 存在问题:
// 1.子类的构造函数的参数,没法传递给父类的构造函数。
// 2.子类的原型的constructor会被改变,需要自己改回来。
// 3.如果父类有引用类型的属性,那么所有的子类会共享这个引用类型的属性。
console.log(c);
</script>
</body>
</html>
ps:(白话)原型链回溯时能找到想要的。
原理图:
2.组合继承模式:组合的原型继承和借用构造函数继承 (解决“ 1.原型继承模式” 中的存在的问题1)
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>对象的继承</title>
</head>
<body>
<script>
// 1.原型继承模式
function Animal(age, name){ // 动物基类
this.age = age;
this.name = name;
}
Animal.prototype.run = function(){ // 在动物基类的原型上添加方法run
console.log(this.name + " is running!");
};
function Cat(age, name){
this.age = age;
this.name = name;
}
Cat.prototype = new Animal(); // 原型的继承方式,由于Animal的constructor指向的是Animal构造函数,
// 故,这里还要改回来 !
Cat.prototype.constructor = Cat;
var c = new Cat(12, 'iser');
c.run(); // 从Animal原型上继承的方法
// 存在问题:
// 1.子类的构造函数的参数,没法传递给父类的构造函数。
// 2.子类的原型的constructor会被改变,需要自己改回来。
// 3.如果父类有引用类型的属性,那么所有的子类会共享这个引用类型的属性。
console.log(c);
// 2.组合继承模式:组合的原型继承和借用构造函数继承 (解决“ 1.原型继承模式” 中的存在的问题1)
// 父类
function Animal1(age, name){
this.age = age;
this.name = name;
this.foos = ['veg', 'ani'];
}
// 在父类的原型上创建一个run方法
Animal1.prototype.run = function(){
console.log(this.name + " is running!!!");
};
// 定义子类
function Cat1(age, name){
// Animal(age, name); // 函数执行模式,此时 Animal里的this 指向window
// 借用父类的构造函数给子类创建实例属性;第①次执行父类的构造函数
Animal.call(this, age, name); //解决函数执行模式的问题: 把this传过去
}
Cat1.prototype = new Animal(); // 第②次执行父类的构造函数
Cat1.prototype.constructor = Cat1;
var c1 = new Cat1(12, 'oerd');
c1.run();
// 存在的问题:
// 1.子类的原型的constructor会被改变,需要自己改回来。
// 2.如果父类有引用类型的属性,那么所有的子类会共享这个引用类型的属性。
// 3.父类的构造函数被执行了两次(完美控不能忍)。
</script>
</body>
</html>
3.原型式继承
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>对象的继承</title>
</head>
<body>
<script>
// 3.原型式继承模式
function object(o){ // o就是要借用的对象
function F(){}
F.prototype = o; // 让空函数的原型指向o对象
return new F(); // 创建一个f实例,f的内部原型指向o对象
}
var m = {age: 19, name: 'lili', friend: ['gir', 'ier']};
var m1 = object(m);
console.log(m1.friend); // Array [ "gir", "ier" ]
m1.age = 20;
console.log(m1); // 由于原型链的写规则,自己没有就自己创建,故不会对m造成影响!
console.log(m);
// 优点:
// 不需要使用new构造函数就可以直接构造其他对象。
// 缺点:
// 所用构造出来的实例会共享原型对象上的引用类型的属性。
// 原型式继承模式:在EC5中被标准化了,可以直接用Object.create();
</script>
</body>
</html>
4.寄生继承模式
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>对象的继承</title>
</head>
<body>
<script>
// 4.寄生继承模式:
// 传一个对象到一个方法(工厂方法)。方法内部根据传来的对象构造一个新对象,并对新对象进行扩展增强。返回新对象。
function createPerson(p){
var o = object(p); // 通过p对象构造一个新对象o
o.say = function(){ // 对新构造出来的对象o进行扩展
console.log("hi");
}
return o;
}
</script>
</body>
</html>
5.[重点] 寄生组合继承模式
略。详见:寄生组合继承模式