面向对象(00)
面向对象中的对象,就是将构造函数,把相同的功能进行整合,方便复用。
对象属性,是对象内部才用的特征,这种属性有两种。
1、对象属性
数据属性:
Object.defineProperty(person, "name", {
configurable : true , //delete删除属性,默认是true
enumerable : false, //for-in 循环返回属性,默认是true
writable : false, //是否可以修改值
value : "li",//值
})
console.log(person.name);
person.name = "dong";
console.log(person);
访问器属性:
var person = {
_year : 0
}
Object.defineProperty(person, "year", {
configurable : true ,
enumerable : false,
get : function(){
return this._year - 2;
},
set : function(newValue){
this._year = newValue + 1000;
}
})
person.year = 10;
console.log(person.year);
2、创建对象
所谓的创建对象就是把相同归类相同的功能和属性。具体怎么去实现呢?
(1)工厂模式
(2)构造函数
(3)原型模式
(4)组合模式
3、继承
(1)原型链继承
是把父类的属性和方法实例化后,赋给子类的原型上。这样在子类的原型上,就存在父类的属性和方法。
这样的缺点就是,一旦其中一个子类的原型上属性和方法被改变。那么其他的子类上的属性和方法也都将会被改变。
//属性
function SuperType(){
this.colors = ["red","blue","green"]
}
//方法
SuperType.prototype.getSuperValue = function(){
return this.prototype
}
function SubType(){
}
SubType.prototype = new SuperType();
//添加子类方法
SubType.prototype.getSubValue = function(){
return this.subprototype;
}
//重写超累方法
SubType.prototype.getSuperValue = function(){
return false;
}
var instance1 = new SubType();
instance1.colors.push('black');
var instance2 = new SubType();
console.log(instance2.colors)
//此时打印出来的是
Array(4)
0:"red"
1:"blue"
2:"green"
3:"black"
(2)借用构造函数的方法去实现继承
在子类中构造构造函数去调用父类的构造函数,然后将父类的属性和方法挂载到子类上。注意,原型链上的属性和方法不能被继承。
所以这种方式的缺陷是,方法写在构造函数上,每一次执行都被拷贝一份,造成资源的浪费。
//属性
function SuperType(){
this.colors = ["red","blue","green"]
}
//子类用构造函数去执行父类,将对象和方法挂载到
function SubType(){
SuperType.call(this)
}
var instance1 = new SubType();
instance1.colors.push('black');
var instance2 = new SubType();
console.log(instance2.colors)
(3)组合继承
把属性通过借用构造函数的方式去进行继承,把方法通过原型链的方式去进行继承
其中在用原型链继承方法的时候定义一个空的构造函数去清空父类的属性,避免在原型中在继承一次,造成冗余。
//父类属性
function SuperType(name){
this.colors = ["red","blue","green"],
this.name = name
}
//父类方法
SuperType.prototype.SayName = function(){
console.log("name")
}
//子类通过借用构造函数去继承属性
function SubType(name,age){
SuperType.call(this,name);
this.age = age; //定义子类里面自由的属性
}
//子类通过原型链去继承方法
// SubType.prototype = new SuperType();
//如果直接这么继承会在原型链中继承属性,造成冗余。
function F(){ } //声明一个空方法,相当于清空了父类的属性。
F.prototype = SuperType.prototype;
SubType.prototype = new F();
SubType.prototype.constructor = SubType;
function F(){ } //声明一个空方法,相当于清空了父类的属性。
F.prototype = SuperType.prototype;
SubType.prototype = new F();
可以用Object.create(SuperType.prototype)去代替
Object.create() 接受一个参数proto,将新对象的proto指向传进来的。
//(5) ES6继承
class fn3{
constructor(age){
this.age = age;
}
say(){
console.log(this.name)
}
}
class SubFn extends fn3{
constructor(name,age){
super(age)
this.name = name;
}
eat(){
console.log(this.name)
}
}
var obj = new SubFn('li',[23,23]);
console.log(SubFn.prototype)
console.log(obj)
原型和原型链之间的关系:
定义对象的三种方式:
var o ={ }; //是 new object()的简写
var o1 = new Base();
var o2 = Object.create(Base);
new开始去理解,
当用new去实例一个对象的时候,其实发生了这么几个事情。比如 // new Base();
(1)首先生成一个空对象,可以理解为 var obj = {};
(2)对象的上的原型属性(proto)指向构造函数的原型属性(prototype)
(3) 使用obj去调用 // Base.call(obj);
(4) 如果构造函数没有返回值或者返回值不是对象的话,那个这个对象就是obj,是对象的话,则会被覆盖。
所以在原型链继承上,因为一个对象实例的proto指向其构造函数的prototype,所以把实例付给新的构造函数的prototype。这样新的构造函数在原型属性上就继承了之前的方法和属性。
Object.create(prototype, descriptors)
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
这种写法的好处是指定了构造函数prototype的指向,不会真的去执行构造函数。
console.log(obj1 instanceof Abc)
console.log(obj1 instanceof BBC)
console.log(obj1 instanceof Object)
可能都是true
hasOwnProperty
obj.hasOwnProperty("name") // 判断属性是否是自己实例属性,是为true
in
isPrototypeOf()
function base(argument) {
this.name = "li"
}
var a = new base();
console.log(base.prototype.isPrototypeOf(a))//base.prototype函
数在a的原型链上
function base2(){
}
base2.prototype = new base();
var a2 = new base2();
console.log(base2.prototype.isPrototypeOf(a2))//base2.prototype函数在a的原型链上
console.log(base.prototype.isPrototypeOf(a2))//base.prototype函数在a的原型链上
console.log(Object.prototype.isPrototypeOf(a2))//base.prototype函数在a的原型链上