1. 概念
面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。前面提到过,ECMAScript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为这样(以及其他将要讨论的原因),我们可以把ECMAScript的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数。
2.对象的创建
var obj = {};
var obj = new Object();
添加属性
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
alert(this.name);
};
2.1封装函数,工厂模式
工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。考虑到在ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节。
function Person(name,age){
var obj = {}; 采集原料
//加工
obj.name = name;
obj.age = age;
obj.say = function(){
alert(this.name);
}
//出厂
return obj;
}
2.2 类的实例化,就等于是在调用函数
//类的实例化,就等于是在调用函数
var kk = Person('开开',7);
kk.say();
var st = Person('神童',-2);
st.coding = function(){
alert('码')
}
st.say();
st.coding();
但是这样,这两个类之间依然没有什么内在的联系, 不能反映出他是同一个原型对象的实例。
于是Javascript提供了一个构造函数(Constructor)
模式。
2.3 构造函数(Constructor)
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
一般来说,构造函数的首字母会大写
于是,上面的函数现在可以这么写
function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
alert(this.name);
}
}
这样,我们就可以生成实例对象了
var kk = new Person(); //类的实例化
生成实例的时候的new是一个专门用来运算函数的 一元运算符
2.4 new
new是一个专门用来运算函数的 一元运算符
加了new和不加new有什么区别:
不new之前
- 函数中的this指向window
- 必须要括号,为了调用
- 如果没有对象,默认返回值是未定义。
加new
- this指向构造函数的对象
- 可以不加括号(加括号为了传参)
- 系统默认创建一个对象,对象变成了this,然后return 了这个this(对象、类)。
如果return一个简单类型的数据,那么返回值不会是这个数据,而是这个对象(类)。如果return一个复合类型的数据,那么返回值就为return后面的对象
2.5 Prototype
构造函数方法很好用,但是存在一个浪费内存的问题。
为了解决这个问题,Javascript规定,每一个构造函数都有一个prototype
属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype
对象上。
我们可以这样去书写函数:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
alert(this.name);
}
然后生成实例:
var kk = new Person('kk',23);
kk.say();
var st = new Person('神童',2);
st.say();
这个时候所有的say方法都会指向对象的同一个内存地址,这样就提高了运行效率。
alert(kk.say == st.say); //true
参考链接:
阮一峰的网络日志 >Javascript 面向对象编程(一):封装
3.原型链
__proto__
当一个函数被声明的时候,自动会有prototype
当一个对象被创建的时候,是没有prototype只有原型链(__proto__
)
每一个javascript对象都有一个原型,对象里包含了一个私有属性,指向对象原型的指针,当然,这个“原型”也是一个对象
每次查找该对象的属性时,如果每没有找到就会自动找原型里的同名属性,还没找到就找原型的原型,以此类推
这就叫做javascript的“原型链”
查找实例化对象下的方法:
先看看这个实例化对象上有没有这个方法。
如果有,那么就直接调用
如果没有,那么会通过__proto__
去找它的构造函数的prototypr
有没有这个方法。
如果有,就直接调用。
如果没有,那么就会通过这个prototype
的__proto__
继续向上查找
function Person(name,age){
this.name = name;
this.age = age;
}
Object.prototype.say = function(){
console.log("obj.say");
}
Function.prototype.say = function(){
console.log("fun.say");
}
var st = new Person('神童',2);
st.say();
console.log()
在上面的例子中,st.say会输出obj.say,而Person.say会输入fun.say
查找顺序为:
st.say ->Person.prototyor(实例st的构造函数) ->Person.prototype.__proto__ (实例st的构造函数的原型对象的原型链)-> Object.prototype
Person.say -> Function.prototype(函数Person的构造函数)
如果上面的例子中没有Function的say方法,那么会通过Function的__proto__去找到Object.prototype
注意,函数下既有prototype
也有__proto__
,但是prototype
是给这个构造函数的实例用的,所以如果函数自身调用方法,不会找自己的prototype
,而是直接去超找__proto__
。
4. this
函数名+括号的时候,先看看前面有没有赋值调用(主),如果有,那么就指向主,如果没有就指向window
然后在看有没有new 如果有new就指向实例化对象,如果没有就指向window。
定时器:
如果函数前没有new,那么指向window
如果函数前有new,那么指向实例化对象
事件(on,addEventListener):
this指向事件触发的对象。
如果在这个事件中直接调用函数,那么就执行函数+括号的判断。
一般解决this的问题:
- 清楚this是谁
- 将想使用的this存为一个变量使用
网友评论