面向对象-1

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之前

  1. 函数中的this指向window
  2. 必须要括号,为了调用
  3. 如果没有对象,默认返回值是未定义。

加new

  1. this指向构造函数的对象
  2. 可以不加括号(加括号为了传参)
  3. 系统默认创建一个对象,对象变成了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的问题:

  1. 清楚this是谁
  2. 将想使用的this存为一个变量使用

你可能感兴趣的:(面向对象-1)