js 面向对象
类和对象:
对象:对象是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物
对象的创建:
// 1. 字面量
var zhang = {
name : 'zhang',
age : 18
}
// 2. 构造函数创建对象
function Stu(name, age) {
this.name = name;
this.age = age;
}
var zhang = Stu('zhang' , 18);
类:ES6新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象
// 创建类
class Stu {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 类方法
eat() {
console.log("在吃饭");
}
}
// 实例化
var student = new Stu('zhang' , 18);
继承
用extends关键字,子类可以继承父类的属性和方法
super关键字,可以访问父类的属性和方法
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
add() {
console.log(this.x + this.y);
}
print() {
console.log("输出");
}
}
class Son extends Father {
constructor(x, y) {
super(x, y); // 调用父类的构造函数 super必须放在this之前
this.x = x;
this.y = y;
}
}
var son = new Son(1, 2);
son.add(); // 继承父类的add方法
this指向问题
注意,在类中谁调用了函数,this指向调用函数的对象
- constructor中的this指向的是new出来的实例对象
- 自定义的方法,一般也指向的new出来的实例对象
- 绑定事件之后this指向的就是触发事件的事件源
构造函数和原型
在ES6之前没有类,通过构造函数和原型可以模拟类
构造函数
通过构造函数创建对象,用new,实际上是先创建了一个空对象,然后另this指向这个空对象,执行构造函数的语句,给这个空对象添加属性和方法,返回对象
// 构造函数
function Star(name, age) {
this.name = name;
this.age = age;
this.sing = function() {
console.log("我唱歌超好听");
}
}
// 实例化 对象
var angela = new Star('Angela Zhang', 18);
console.log(angela.name);
angela.sing();
静态成员和实例成员
- 实例成员是用this.添加的属性和方法,是属于实例化对象的,通过实例化对象去调用,比如上面程序中的sing方法,name属性都是实例成员
- 静态成员是通过 构造函数名.成员名 添加,属于构造函数,通过 构造函数 调用
Star.sex = '女'; // 给Star构造函数添加属性sex
console.log(Star.sex); // 调用静态成员
原型
构造函数存在空间浪费的问题,因为对于复杂数据类型的成员,在实例化对象时,会为每一个对象都开辟一块空间,存放相同的函数
解决方法:原型prototype。原型是一个对象,构造函数中有一个属性prototype,即构造函数的原型对象,可以将实例化对象共有的方法放在原型对象中,解决空间浪费的问题。
function Star(name, age) {
this.name = name;
this.age = age;
}
Star.prototype.sing = function() { // 将sing方法放在原型对象中
console.log("我唱歌超好听");
}
// 实例化
var angela = new Star('angela zhang', 18);
var zsh = new Star('zhang shaohan', 18);
console.log(angela.sing === zsh.sing); // true
angela.sing(); // 实例化对象可以访问构造函数原型对象中的方法
####### 实例化对象的__proto__属性
实例化对象中有一个属性__proto__,该属性是一个对象,指向的是构造函数的原型对象prototype
console.log(zsh.__proto__ === Star.prototype);// ture
原型对象中的constructor属性
原型对象中有一个属性constructor指向的是构造函数
console.log(Star.prototype.constructor); // Star
在赋值修改了原型对象时,可以利用constructor手动使原型对象指向构造函数
// 这样修改原型对象,会使之前的内容被覆盖,原型对象中就没有的了constructor属性,需要手动添加constructor属性,让他指向构造函数
Star.prototype = {
constructor: Star,
sing: function() {
console.log("唱歌");
},
dance: function() {
console.log("跳舞");
}
}
原型链
原型对象也是一个对象,也有__proto__属性,指向Object构造函数的prototype原型对象,Object的原型对象的__proto__属性为null
原型链及成员查找方法:
实例化对象先查找自己有没有该成员
若没有,则查找__proto__原型对象是否有该成员
若没有,则查找原型对象的__proto__,即Object的prototype原型对象是否有该成员
若没有,则返回undefined
关于构造函数和原型对象中的this指向问题
构造函数的this,指向实例化对象
原型对象中的函数的this,也指向实例化对象
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var that;
Star.prototype.sing = function() {
console.log('我会唱歌');
that = this;
}
var ldh = new Star('刘德华', 18);
// 1. 在构造函数中,里面this指向的是对象实例 ldh
console.log(that === ldh);//true
// 2.原型对象函数里面的this 指向的是 实例对象 ldh
继承
####### call()方法
- call() 可以用来调用函数
function fn() {
}
fn.call(); // 调用函数 等同于fn()
- call(targetThis, args) 可以用来手动修改函数的this指向
function fn() {
console.log(this); // this 为 window
}
fn.call(); // 调用函数
var obj = {
name: 'hhh';
}
function fn(x,y) {
console.log(this); // this指向obj
console.log(x+y);// 3
}
fn.call(obj,1,2); // 修改fn中的this指向
用call() 来继承父构造函数中的成员
// 父构造函数
function Father(name, age) {
this.name = name;
this.age = age;
}
// 子构造函数
function Son(name, age){
// 用call调用父亲的构造函数,并修改this指向为儿子的实例化对象
Father(this, name, age);
}
// 实例化
var son = new Son('zhang',18);
console.log(son.name);
继承父类的prototype中的公共方法
思路:让子类Son的prototype原型对象拥有父类的prototype中的方法。
注意:不能另子类的prototype直接等于父类的prototype,即
Son.prototype = Father.prototype, 因为这样其实是二者指向了同一个对象,修改子类的prototype也会影响父类的prototype
解决方法:Son.prototype = new Father()
即让子类的原型对象是父类的实例化对象,这样子类的实例化对象就能通过__proto__查找到父亲的实例化对象,而父亲的实例化对象又能通过它的__proto__访问到父亲原型对象中的方法,从而实现继承
注意:在给Son.prototype赋值之后,还应手动添加constructor属性,指向Son构造函数
// 父构造函数
function Father(name, age) {
this.name = name;
this.age = age;
}
Father.prototype.sing = function() {
console.log("唱歌");
};
// 子构造函数
function Son(name, age){
// 用call调用父亲的构造函数,并修改this指向为儿子的实例化对象
Father(this, name, age);
}
Son.prototype = new Father(); // 继承父亲的方法
Son.prototype.constructor = Son; // 手动添加constructor
// 实例化
var son = new Son('zhang',18);
son.sing(); // 成功继承父亲的sing方法