面向对象(OOP):具有灵活,代码可复用性,高度模块化等特点
1.对象是个单个实物的抽象
2.对象是一个容器,封装了对应的属性和方法
3.属性是对象的状态,方法是对象的行为(要完成的任务)
面向对象的三个特性:封装,继承,多态
构造函数
实例化出来的对象内部的属性constructor(构造器)属性指向了当前的构造函数
如果想生成一个对象需要一个模板(表示一类实物的共同特征)让对象生成
类(class) 就是对象的模板
JS不是基于类的,而是基于构造函数constructor(构造器)和原型链(prototype)
构造函数的名字的第一个字母都大写
构造函数特点:1.函数特内使用this关键字,代表了所要生成的对象实例
2.生成对象,必须使用new关键字实例化
function Dog(name,age){
//name和age 就是当前实例化对象的属性
this.name = name;
this.age = age
}
var dog1 = new Dog('阿黄',10);
1.__proto__是实例对象的原型
2.prototype是构造函数的原型
new的实现原理
1.首先创建一个空的对象,作为将要返回的对象实例
2.将这个空对象的__proto__(原型)指向构造函数的prototype(原型)
3.让this变量指向这个新创建的实例对象
4.然后以这个新创建的对象为上下文执行构造函数
5.最后返回这个创建的对象
prototype和__proto__和constructor
1.__proto__和constructor属性是对象所独有的,prototype属性是函数所独有的。
2.因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。
3.prototype属性是可以给函数和对象添加可共享(继承)的方法、属性。
4.__proto__是查找某函数或对象的原型链方式。
5.constructor这个属性包含了一个指针,指回原构造函数,也表示原型对象和构造函数之间的关联关系
原型链
实例对象和原型对象之间的关系是通过__proto__原型来联系起来的,这个关系就是原型链。原型链的最终指向是null
prototype和__proto__和constructor的作用
1__proto__的作用就是当访问一个对象的属性时,如果在该对象的内部找不到这个属性,那么它就会去它__proto__(原型)属性所指向的那个对象里面找。如果还是找不到那么就一直往上找,直到原型链的顶端null。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
2.prototype的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法。
3.constructor就是继承自原型对象,指向了构造函数的引用,所有的函数最终都指向function
构造函数和实例对象和原型对象的区别
构造函数可以实例化对象
构造函数自动赋予一个prototype属性,该属性指向了实例对象的原型对象
构造函数的原型对象prototype中有一个constructor构造器,该构造器指向的是自己所在的原型对象,所在的构造函数。
实例对象的原型对象__proto__(原型)指向的是该构造函数的原型对象prototype
实例对象可以继承原型对象的属性,所以实例对象也有一个constructor属性,该属性同样指向原型对象对应的构造函数
构造函数的原型对象prototype中的方法是可以被实例对象直接访问的
所有的函数都可以看成是构造函数Function()的new操作的实例化对象。那么,Function可以看成是调用其自身的new操作的实例化的结果
所以,如果Function作为实例对象,其构造函数是Function,其原型对象是Function.prototype
所有的对象都可以看成是Object()构造函数的new操作的实例化对象
1.函数(Function也是函数)是new Function的结果,所以函数可以作为实例对象,其构造函数是Function(),原型对象是Function.prototype
2.对象(函数也是对象)是new Object的结果,所以对象可以作为实例对象,其构造函数是Object(),原型对象是Object.prototype
3.Object.prototype的原型对象是null
创建对象的五种方式
1.对象字面量
// new构造函数
var obj = new Object();
obj.name = 'xx'
console.log(obj) //打印Object 里面有name属性 和 __proto__属性
//对象字面量
var person = {
name = 'xx';
age = 18
}
console.log(person); //{name : 'xx' , age : '18'}
console.log(person.name) // xx;
Object.create(对象) 从一个实例对象生成另一个实例对象
//Object.create(对象) 从一个实例对象生成另一个实例对象
//create()中的参数a作为返回实例对象b的原型对象,在a中定义的属性和方法,都能被b实例对象继承下来
var a = {
getX:function (){
console.log('x') //x
}
}
var b = Object.create(a);
b.getX();
2.工厂模式
能创建多个类似的对象
它解决不了对象识别的问题
3.构造函数模式
缺点:浪费内存空间,相同的方法在不同的实例中调用时占用了不同的内存空间/
①构造函数拓展模式
定义多个全局函数,严重污染全局空间
②寄生构造函数模式
特点:结合工厂模式和构造函数模式。首先创建一个函数,函数内部实例化一个对象,并且将对 象返回,在外部的使用 使用new来实例化对象
问题:定义了相同的方法,浪费内存空间
instanceof运算符和prototype属性都没有用
③稳妥构造函数模式
没有公共属性,并且他的方法不引用this指向
就像是一个闭包一样,结合闭包使用
4.原型模式
特点:在于方法可以被共享
问题:在于引用类型值属性会被所有的实例对象共享并且修改。也就是说如果有一个实例它修改了原型内的方法那么所有实例调用的这个方法都会被修改
function Person(){};
Person.prototype = {
constructor : Person, //只要修改了函数的原型,那么必须也要修改相对应的constructor属性!!!这个是必须加的,只要修改原型必须有这句话。
name : 'xx',
age : 18,
sayName:function () {
console.log(this.name);
}
}
var p1 = new Person();
p1.sayName(); //xx
var p2 = new Person();
p2.sayName(); //xx
5.组合模式
组合模式结合了构造函数模式和原型模式。
可以进行传值
目前用的最多的都是组合模式
function Person(name,age){
//定制当前对象自己的属性
this.name = name ;
this.age = age;
this.friends = ['alex','阿黄']
};
//定制各个实例对象的共享属性
Person.prototype = {
constructor : Person;
sayName:function(){
console.log(this.name);
}
}
var p1 = new Person('xx',18)
var p2 = new Person('ff',18)
console.log(p1,p2) //Person{name:'xx',age:'18',__proto__{}}Person{name:'ff',age:'18',__proto__{}}
p1.friends.push('小红')
console.log(p1.friends) // "'alex','小黄','小红'"
console.log(p2.friends) //"'alex','小黄'"
5.1动态原型模式
改变初始化原型对象
function Person(name,age){
//定制当前对象自己的属性
this.name = name ;
this.age = age;
this.friends = ['alex','阿黄'];
if(typeof this.sayName != 'function'){
//初始化原型对象上的属性
Person.prototype.sayName = function (){
console.log(this.name)
}
}
};
封装
类其实就是保存了一个函数的变量,这个函数有自己的属性和方法。将属性和方法组成一个类的过程就是封装。
封装:把客观事物封装成抽象的类,隐藏属性和方法的实现细节,仅对外公开接口。
封装实现的三种方式,分别是构造函数、原型、在类的外部通过.的语法添加
继承
JavaScript继承机制是通过原型对象实现继承,继承就是一种父类级别跟子类级别的关系,子类可以继承父类的属性和方法。原型有两个作用,一是数据共享,节省内存空间。二是为了实现继承。继承也是为了实现数据的共享
**继承有四种继承**:
原型链继承
特点:原型对象所有的属性和方法,都能被实例共享
本质:重写原型对象,将一个父对象的属性和方法作为一个子对象的原型对象的属性和方法
问题:①在父类中定义的实例引用类型的属性,一旦被修改,其他的实例也会被修改
②实例化子对象的时候,不能向父类型的构造函数传参
借用构造函数继承
特点:在子类的构造函数内部间接调用(call(),apply(),bind())父类的构造函数
原理:就是改变父类中this的指向
好处:只是把父类中的实例属性当作子类的实例属性,并且可以传参
问题:父类定义的共享方法不能被子类所继承下来
组合继承
特点:就是结合原型链继承和借用构造函数继承的优点,并且可以解决原型链和借用构造函数的问题
原型链继承的优点是公有的方法都能被继承下来
借用构造函数的优点是实例属性都被子类继承下来
优点:重写原型对象,把父类的共享方法继承下来
问题:无论在什么情况下,都会调用父类构造函数两次
①一个是初始化子类对象的时候
②另一个是在子类构造函数内部调用父类的构造函数的时候 (好的)
寄生组合式继承
它是用来解决组合继承的问题
原理就是:把子类的原型对象指向了父类的原型对象
使用方式:b.prototype = Object.create(a.prototype) 将a对象作为b实例的原型对象
多重继承
通过Object.assign(targetObj,copyObj)实现将一个函数的原型复制到想要得到继承的函数的原型上
例:Object.assign(Me.prototype,You.prototype) 意思是将You函数原型内的方法赋值到Me原型上去,这样Me函数就能通过调用原型的方式来实现You函数原型里面的方法