在学习了
js
高级以及es6
之后,再来学习面向对象编程,之前在学习es6
,canvas
的时候已经有接触到了这种思想,感觉还是需要在深入的学习一下,这部分内容牵扯到很多原型链部分的东西,当做复习一下还是很不错的!
ES5
中的面向对象面向对象编程(OOP)
具有灵活、代码可复用、高度模块化等特点。
对象是单个实物的抽象
对象是一个容器,封装了对应的属性和方法,属性是对象的状态,方法是对象的行为(完成的任务)
在
ES5
中没有class
类的概念,所以面向对象是基于构造函数和原型链的,
注意:
构造函数的名字的第一个字母要大写
特点
this
关键字,代表了所要生成的对象实例new
关键字实例化function Dog() {
this.name = name;
this.age = age;
}
var dog1 = new Dog('dw', 10)
如果没有使用
new
关键字,则结果会是undefind
,原因是该函数没有返回值
instanceof
的用法
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。返回true
orfalse
通过instanceof
来判断当前的的对象是否是实例化出来的,如果是实例化出来的this
指向实例化出来的对象,也就是这里的Person
,否则作为普通函数来说当前的this
指向window
function Person(name, age){
if(this instanceof Person){
// this指向了当前的实例,外部使用了关键字new
this.name = name;
this.age = age;
}else{
// this指向了window,外部没有使用了关键字new
return new Person(name,age);
}
new
命令内部原理this
,继承函数原型this
指向构造函数的对象实例,执行构造函数内容为新对象添加属性和方法var obj = {}//创建空对象
obj.__proto__ = Person.prototype;//继承作用域
Person.call(obj,)//改变this指向
return obj //返回对象
constructor
属性每个对象在创建时都会自动拥有一个构造函数属性constructor
constructor是对象__proto__
上的一个属性(如果该对象是函数,则在其prototype
上),通常指向生成这个对象的函数,也就是指向构造函数的引用
obj.constructor === Obj
function Foo(){};
var foo = new Foo();
原型对象:
Foo.prototype
实例对象:
foo
就是实例对象,每一个原型对象中都有一个__proto__
,每个实例对象都有一个constructor
属性,这个constructor
通过继承关系继承来的,它指向了当前的构造函数Foo
构造函数:
用来初始化新创建对象的函数,Foo
是构造函数,自动给构造函数赋予一个属性prototype
,该属性指向了实例对象的原型对象
function Foo(){}
var foo = new Foo()
prototype
属性的作用继承机制:通过原型对象(prototype)实现继承
原型对象的作用,就是定义所有实例对象共享的属性和方法
Foo.prototype.name = 'ljc';
tips:
所有的对象都有自己的原型对象
原型链:对象的原型 => 原型的原型 => 原型的原型的原型 => null
所有的对象都继承了
Object.prototype
上的属性和方法
查找属性和方法的规则:js
引擎会先寻找对象本身的属性和方法,如果找不到就到它的原型对象去找,如果还是找不到,就到原型的原型去找,如果直到最顶层的Object.prototype
还是找不到,就会返回undefined
constructor
属性注意点:一旦我们修改构造函数的原型对象,为防止引用出现问题,同时也要修改原型对象的constructor属性
function MyArray(){};//构造函数
MyArray.prototype = Array.prototype;//复制数组的原型对象,获得方法
MyArray.prototype.constructor = MyArray;//改变constructor指向
var arr = new MyArray();//实例化对象
var Person = { name: 'ljc' }
new
关键字var obj = new Object();
obj.name = 'ljc';
console.log(obj);
Object.create(对象)
var a = {
getName:function(){
console.log('ljc');
}
}
var b = Object.create(a);
b.getName();
缺点
:代码冗余
优点:
能够创建多个类似的对象
function createObj(name,age) {
var o = new Object();//创建对象
//添加属性和方法
o.name = name;
o.age = age;
o.sayName = function() {
console.log(this.name);
}
//返回对象
return o
}
var obj = createObj('ljc',19)//每次调用返回一个对象
缺点:
所有的constructor
指向都相同,没有解决对象识别问题
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name);
}
}
var man = new Person('ljc',19);
var woman = new Person('dw',18);
通过在外部使用new
关键字,将属性和方法通过this
绑定到相应的对象上,解决了工厂模式的遗留问题
缺点:
每个对象都会有一个sayName
方法,执行的功能是一样的,但是却仍然占用了不同的内存空间,浪费了内存资源
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
将sayName
方法定义成全局函数,解决了内存浪费的问题
缺点
:污染全局空间
function createObj(name,age) {
var o = new Object();//创建对象
//添加属性和方法
o.name = name;
o.age = age;
o.sayName = function() {
console.log(this.name);
}
//返回对象
return o
}
var obj = new createObj('ljc',19)
结合了工厂模式和构造函数模式:创建一个函数,函数体内部实例化一个对象,并且将对象返回,在外部使用new
来实例化对象
缺点
:由于没有使用构造函数,所以instanceof
运算符和prototype
属性没有意义
function Person(name){
var a = 10;
var obj = new Object();
obj.sayName = function(){
console.log(a);
console.log(name);
}
return o;
}
var p1 = Person('ljc');
p1.sayName();
没有公共属性,并且它的方法也不引用this
,name
属于函数内部的私有属性,有点像闭包,p1
被称为稳妥对象
缺点
:由于没有使用构造函数,所以instanceof
运算符和prototype
属性没有意义
将属性方法绑定在
prototype
上,实现共享
注意:
需要改变constructor
指向
function Person(){}
Person.prototype = {
constructor: Person,
name: 'ljc',
age: 19,
friends:['dw','xy'],
sayName:function(){
console.log(this.name);
}
}
var me = new Person();
var you = new Person();
每个通过Person
实例化出来的对象都会继承函数的原型
缺点
:当我们修改其中一个对象的属性时,另一个对象下的也会被修改
me.friends.push('jc');
console.log(you.friends);//["dw","xy","jc"]
认同度最高的一种创建自定义对象的模式
function Person(name,age){
// 定制当前对象自己的属性
this.name = name;
this.age = age;
this.friends = ['dw','xy'];
};
//定制公共的方法
Person.prototype = {
constructor: Person,
sayName: function(){
console.log(this.name);
}
}
var wo = new Person('wo', 18);
var you = new Person('you', 20);
解决了原型模式状态共享问题,将私有的属性定义在函数内部,共有的方法通过原型去实现继承引用