面向对象的理解
面向对象的理解
1.面向对象是一种编程思想,相对于面向过程而言
2.将具体的功能封装于对象之中,让对象实现具体功能
原型:
创建一个新的函数,就会根据一组特定的规则,为该函数创建一个prototype属性, 这个属性指向函数的原型对象,在默认情况下所有原型对象都会有一个constructor(构造)属性 这个constructor属性是一个指向prototype属性所在函数的指针。
原型链:
原型链是实现继承的主要方式,每一个构造函数都有一个原型对象,原型对象有一个指针指向构造函数,实例有一个指针(内部指针)指向原型对象。假设把一个原型对象A等于另外一个类型的实例b,这个时候A原型对象指向B原型对象然后指向B构造函数,在假设B原型也是另一个原型C的实例,层层递进,形成原型链
继承
通过原型原型链等实现一个对象继承获取另一个对象的属性和方法叫做继承
面向对象的三个特点
封装 ----隐藏对象的属性和实现细节,对外提供公共的访问方式
1.原始的封装模式Object的构造函数或者对象字面量
2.改进原始模式-工厂模式
3.构造函数(Constructor)模式
4.改进构造函数-原型模式
继承----
1.原型链继承
2.借用构造函数继承-伪造对象法-经典继承法
3.组合继承
4.原型式继承
5.寄生式继承
6.寄生组合式继承
多态----
同样的操作在不同的对象上可以产生不同的解释和不同的执行结果
封装-创建对象的方法
1.原始的封装模式Object的构造函数或者对象字面量
原型对象:
var Cat = {
name : '',
color : ''
}
实例对象:
var cat1 = {}; // 创建一个空对象
cat1.name = "大毛"; // 按照原型对象的属性赋值
cat1.color = "黄色";
var cat2 = {};
cat2.name = "二毛";
cat2.color = "黑色";
原始封装缺点: 实例多的时候比较麻烦;实例和原型之间看不出来有什么联系
即---如何识别对象的类型(这个类型指的对象的具体作用,而不是数据的类型object的意思)
2.改进原始模式-工厂模式
原型对象
function Cat(name,color) {
return {
name:name,
color:color
}
}
实例对象
var cat1 = Cat("大毛","黄色");
var cat2 = Cat("二毛","黑色");
用函数解决多实例生成问题,但是还是看不出来原型和实例之间的关系
3.构造函数(Constructor)模式
所谓"构造函数",其实就是一个普通函数,但是内部使用了this
变量。对构造函数使用new
运算符,就能生成实例,并且this
变量会绑定在实例对象上。
关于构造函数和普通函数的解释:
构造函数和普通函数的唯一区别就在于调用方式不同,构造函数也是函数不存在定义构造函数的特殊语法,任何函数只要通过new操作符来调用,那它就可以作为构造函数,任何函数不通过new 调用那跟普通函数不会有什么区别。
原型对象
function Cat(name,color){
this.name=name;
this.color=color;
}
实例对象
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.name); // 大毛
alert(cat1.color); // 黄色
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true
用这种办法生成的实例会含有一个constructor
属性(构造函数属性)指向他们的构造函数就是原型
4.改进构造函数-原型模式
构造函数的问题是对于每一个实例对象属性type不变 函数也不变,
type属性和eat()方法都是一模一样的内容,每一次生成一个实例,
都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。
function Cat(name,color){
this.name = name;
this.color = color;
this.type = "猫科动物";
this.eat = function(){alert("吃老鼠");};
}
解决办法
让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
理解原型对象:创建一个新的函数,就会根据一组特定的规则,为该函数创建一个prototype属性,
这个属性指向函数的原型对象,在默认情况下所有原型对象都会有一个constructor(构造)属性
这个constructor属性是一个指向prototype属性所在函数的指针。
举例:
Person 构造函数>>自动拥有>>Person.prototype属性>>指向>>原型对象>>自动具有>>Person.prototype.constructor属性(指针)>>指向>> Person构造函数
原型对象
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){alert("吃老鼠")};
实例对象
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠
所有实例的type属性和eat()方法,其实都是同一个内存地址,
指向prototype对象,因此就提高了运行效率。
alert(cat1.eat == cat2.eat); //true
原型模式存在的问题:在prototype上的属性,无法用传递参数的方式进行初始创建。所有实例上默认初始取得的属性值都是相同的,对于属性是基本值可以在实例上重新赋值隐藏原型属性值,但是对于原型上属性值是引用类型的属性(内存地址相同,传递的是指针,比如数组)
Person.prototype.friends = ["Q","W"],这种情况我们修改person1.friends.push("E"),person2.friends的值也会被修改,因为内存地址一样,属性值对应的只是指向位置的指针。
5.最终模式-组合使用构造函数和原型模式
构造函数定义实例属性,原型定义方法和共享属性
function Person(){
this.friends = ["w","x"]
}
Person.prototype = {
key: value,
xxx: function(){
}
}
优化上述模式: 动态原型模式 -动态的根据需要添加原型属性
function Person(){
this.friends = ["w","x"];
if( typeof this.sayName != "function"){
Person.prototype.sayName = function(){
}
}
###另外还有寄生构造函数模式,不在详解
}
补充
isPrototypeOf()
这个方法用来判断,某个proptotype对象和某个实例之间的关系
alert(Cat.prototype.isPrototypeOf(cat1)); //true
alert(Cat.prototype.isPrototypeOf(cat2)); //true
hasOwnProperty()
实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,
还是继承自prototype对象的属性。
alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false
in运算符
in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。
alert("name" in cat1); // true
alert("type" in cat1); // true
为实例添加属性,原型中也有相同的属性则原型的属性在调用实例时是不显示的,
比如 Person.prototype.age = 18; person1.age = 19; console.log(person1.age) 19
console.log(person2.age) 18
------------------------------------------------封装部分-----------------------------------------------
继承
针对不同对象之间的关系,其他的对象有我这个对象需要的属性或者方法我需要
因此我需要拿到他的东西
原型链的理解:
原型链是实现继承的主要方式,每一个构造函数都有一个原型对象,原型对象有一个指针指向构造函数,实例有一个指针(内部指针)指向原型对象。假设把一个原型对象A等于另外一个类型的实例b,这个时候A原型对象指向B原型对象然后指向B构造函数,在假设B原型也是另一个原型C的实例,层层递进,形成原型链
介绍继承需要先知道
引用类型的值定义位置的影响引用类型的值定义的位置会影响值的复用性,定义在原型上会导致实例公用一个值
1.原型链继承
原型链继承原型链继承存在的问题:
1.Cat通过原型链继承Animal以后,Cat.prototype就变成了Animal的一个实例 Cat.prototype就具有了自己的colors属性(实例的属性单独存在一个新内存),相当于在Cat.prototype上加上了 一个Cat.prototype.colors,因此导致 Cat的所有实例都会共享同一个属性,对一个实例修改会影响其他实例的值
也就是:A通过原型实现继承B时,B的实例属性会变为现在A的原型属性(B的实例值赋给了A的原型)
2.创建子类型的实例时不能向超类型(A)的构造函数传递参数
2.借用构造函数继承-伪造对象法-经典继承法
借用构造函数在创建新的实例的时候,调用需要继承的构造函数,因此在Cat对象上执行了Animal的所有初始化代码,因此每个实例都会具有自己的colors属性内存
call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。