一、面向对象编程OOP
- 含义:以对象的功能来划分,而不是步骤
- 特性:封装性、继承性、动态性
- 类:抽象了对象的公共部分,通过类实例化一个对象
- 对象:一组无序的相关属性和方法的集合。万物皆对象,一个具体的事物
- 属性:事物的特征
- 方法:事物的行为
二、创建类
- 通过class关键字创建类,类名我们还是习惯性定义首字母大写
- 类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象中
- constructor函数, 只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
- 生成实例, new不能省略
- 最后注意语法规范,创建类, 类名后面不要加小括号,生成实例, 类名后面加小括号,构造函数不需要加function
- 我们类里面所有的函数不需要写function
- 多个函数方法之间不需要添加逗号分隔
< script>
// 1.创建类class 创建 一个明星类
class Star {
//类的共有属性放到constructor里面
constructor(uname, age) {
this.uname = uname ;
this.age = age;
}
//我们类里面所有的函数不需要写function,多个函数方法之间不需要添加逗号分隔
sing () {
console.log('我会唱歌')
}
}
// 2.利用类创建对象new
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友',20);
console . log(1dh);
console. log(zxy);
</script>
三、类的继承
- 程序中的继承:子类可以继承父类的一些属性和方法。
class Father { } // 父类
class Son extends Father { } // 子类继承父类
- super关键字:可以调用父类的构造函数/普通函数
- 子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,再使用子类的构造方法)
- 继承中,如果实例化子类输出一个方法, 先看子类有没有这个方法,如果有就先执行子类的
- 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
class Father {
say() {
return ' 我是爸爸';
}
}
class Son extends Father {
say() {
console.1og(我是儿子);
}
}
var son = new Son ();
son.say();
class Person { // 父类
constructor ( surname) {
this. surname = surname;
}
}
class Student extends Person {
//子类继承父类
constructor (surname, firstname) {
super (surnane) ;
// 调用父类的constructor (surname)
this.firstname = surname; //定义子类独有的属性
}
}
三个注意点:
- 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
- 类里面的共有属性和方法一定要加this使用
- 类里面的this指向问题.
- constructor 里面的this指向实例对象方法里面的this指向这个方法的调用者
四、构造函数和原型
1. 概念
在ES6之前,对象不是基于类创建的,而是用种称为构建函数的特殊函数来定义对象和它们的特征。
创建对象可以通过以下种方式:
- 对象字面量
- new Object0
- 自定义构造函数
2. 构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new 一起使用。我
们可以把对象中一些公共的属性和方法抽取出来 ,然后封装到这个函数里面。
new在执行时会做四件事情:
①在内存中创建一个新的空对象。
②让this指向这个新的对象。
③执行构造函数里面的代码,给这个新对象添加属性和方法。
④返回这个新对象(所以构造函数里面不需要return ) 。
// 利用构造函数创建对象
function Star(uname, age) {
this. uname = uname ;
this.age = age;
this.sing = function() {
console.log( '我会唱歌' );
}
}
var 1dh = new Star('刘德华’,18);
var zxy = new Star('张学友',19);
console .1og(1dh);
1dh.sing();
zxy.sing();
// 1. 实例成员就是构造函数内部通过this添加的成员uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问
console . log(1dh. uname);
ldh. sing();
console. log(Star .uname); //不可以通过构造函数来访问实例成员
// 2.静态成员在构造函数本身上添加的成员 sex 就是静态成员
Star.sex = '男';
console . log(Star.sex); // 静态成员只能通过构造函数来访问
console.1og(1dh.sex); // 不能通过对象来访问
3. 构造函数的问题
构造函数方法很好用,但是存在浪费内存的问题。
4. 构造函数原型prototype
- 构造函数通过原型分配的函数,是所有对象所共享的。
- JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。 注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
- 我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
- 原型是什么?
一个对象,我们也称为prototype为原型对象。 - 原型的作用是什么?
共享方法。
5. 对象原型_ proto_
- 对象都会有一一个属性_ proto_ 指向构造函数的 prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有_ proto_ 原型的存在。
- _ proto_ 对象原型和原型对象prototype是等价的。
6. constructor构造函数
- 对象原型(_ proto_ ) 和构造函数的原型对象( prototype )里面都有一个属性 constructor属性, constructor我们称为构造函数,因为它指回构造函数本身。
- constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
function Star(uname, age) {
this. uname = uname ;
this.age = age;
//很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数
// Star . prototype. sing = function() {
// console. log( '我会唱歌' );
// };
// Star . prototype. movie = function() {
// console . log('我会演电影');
// }
// 如果我们修改了原来的原型对象,给原型对象赋值的是个对象,则必须手动的利用 constructor指回原来的构造函数
Star. prototype = {
constructor: Star,
sing: function() {
console.1og(我会唱歌' );
},
movie: function() {
console.log( '我会演电影');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console .1og(Star. prototype);
console .1og(1dh._ proto__ ) ;
7. 构造函数、实例、原型对象E者之间的关系
8.原型链
9. JavaScript的成员查找机制(规则)
①当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
②如果没有就查找它的原型(也就是_ proto_ 指向的prototype原型对象)。
③如果还没有就查找原型对象的原型( Object的原型对象)。
④依此类推一直找到Object为止( null )。
⑤_ proto 对象原型的意义就在于为对象成员查找机制提供一 个向,或者说一条路线。
10. 扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
<script>
//原型对象的应用:扩展内置对象方法
console.log(Array. prototype);
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
var arr=[1,2,3];
console.log(arr.sum());
</script>
五、继承
1. 概念
ES6之前并没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
2. 借用构造函数继承父类型属性
核心原理:通过call()把父类型的this指向子类型的this ,这样就可以实现子类型继承父类型的属性。
<script>
// call方法
function fn(x, y) {
console.1og( '我想喝手磨咖啡);
console . log(this);
console.1og(x + y);
}
var obj = { name: 'lfl' }
// 1. cal1() 可以调用函数
// fn. call();
// 2. call() 可以改变这个函数的this指向此时这个函数的this就指向了o这个对象
fn.call(obj, 1, 2);
</script>
<script>
//借用父构造函数继承属性
// 1.父构造函数
function Father(uname, age) {
// this指向父构造函数的对象实例
this .uname = uname;
this.age = age;
}
// 2.子构造函数
function Son(uname, age, score) {
// this指向子构造函数的对象实例
Father . call(this, uname, age);
this.score = score;
}
var son = new Son ('刘德华',18, 100);
console . log(son);
</script>
3. 借用原型对象继承父类型方法
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father . call(this, uname, age);
this. score = score;
}
// Son. prototype = Father . prototype;这样 直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father ();
//如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son . prototype . constructor = Son;
//这个是子构造函数专门的方法
Son. prototype.exam = function() {
console.1og('孩子要考试');
}
var son = new Son('刘德华',18, 100);
console. log(son);
console . log(Father .prototype);
console . log(Son. prototype . constructor);