理解 javascript 面向对象
面向对象的能力、概念
能力
- 继承 - 由另一个类(或多个类)得来类的属性和方法的能力 (子孙继承父辈祖辈的能力)
- 多态 - 编写能以多种方法运行的函数或方法的能力(普通话和方言的关系)
- 封装、聚集 - 存储信息到对象,并且对象与对象之间使用消息通信,各自存在信息隐藏
概念
在基于类的面向对象方式中,对象(object)依靠 类(class)来产生。而在基于原型的面向对象方式中,对象(object)则是依靠 构造器(constructor)利用 原型(prototype)构造出来的。
ES6 定义:ECMA-262 把对象(object)定义为“属性的无序集合,每个属性存放一个原始值、对象或函数”,对象也可以包含对象。
构成:在 ECMAScript 中,对象由特性(attribute)构成,特性可以是原始值,也可以是引用值。如果特性存放的是函数,它将被看作对象的方法(method),否则该特性被看作对象的属性(property)。
方法与属性很相似, 不同的是:一个是函数,另一个可以被定义为函数。JavaScript 可用方法作类 如:Function Method(){}。
数据类型
在 ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。ECMAScript 的 5 种基本数据类型即 Boolean、Number、String、Null、Undefined 为原始类型。而复杂数据类型 Object 为引用类型。Object 对象是描述 ECMAScript 对象的唯一一个数据类型,JavaScript 中的每个对象都是 Object 对象的实例且继承它所有的属性和方法。所以理解了 Object 对象,就可以更好地理解其他对象。
Object 对象有下列属性和方法:
constructor
对创建对象的函数的引用(指针)。
Prototype
对该对象的对象原型的引用。对于所有的对象,它默认返回 Object 对象的一个实例。
hasOwnProperty(property)
判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty(“name”))
IsPrototypeOf(object)
判断该对象是否为另一个对象的原型。
PropertyIsEnumerable
判断给定的属性是否可以用 for…in 语句进行枚举。
ToString()
返回对象的原始字符串表示。
ValueOf()
返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 ToString() 的返回值相同。
原始类型的引用类型 - 定义一个变量为 new Boolean(boolean)/new Number(number)/ new String(string) 注意括号中为具体的原始值,即是各个原始类型的引用类型。ECMAScript 还为数字类型、字符类型提供了一些辅助其解析和操作的属性和方法。
创建对象
对象类型:一般来说,可以创建并使用的对象有三种:本地对象、内置对象和宿主对象。本地对象就是 ECMA-262 定义的类(引用类型)包括:Object、Function、Array、Date、RegExp… 等。Global 和 Math 是内置对象(它们也是本地对象,根据定义,每个内置对象都是本地对象)。所有 BOM 和 DOM 对象都是宿主对象。
字面式 (literal notation) 声明是创建对象的最简单方式。
//var array = new Array(1, 2, 3);
var array = [1, 2, 3];
//var object = new Object();
//object.x = 1;
//object.y = 2;
//object.z = -3;
var object = {x:1, y:2, z:-3};
//json 构造语法开发者可以随意地用字面式声明方式来构造一个对象,并对其不存在的属性赋值,或者用 delete 将属性删除。
object.fn = function(){};
delete object["z"];
console.log(object); //{x: 1, y: 2, fn: ƒ()}
通过构造器 (constructor) 创建对象,这里每个构造器实际上是一个函数 (function) 对象,该函数对象含有一个”prototype”属性用于实现”基于原型的继承”和“共享属性”。
function createCar() {
var oTempCar = new Object;
oTempCar.color = "blue";
oTempCar.doors = 4;
oTempCar.mpg = 25;
oTempCar.showColor = function() {
alert(this.color);
};
return oTempCar;
}
var oCar1 = createCar();
var oCar2 = createCar();
function Phone(pColor, pBrand, pPrice){
this.color = pColor;
this.brand = pBrand;
this.pPrice = pPrice;
this.showColor = function(){
alert(this.color);
}
}
var oPhone = new Phone("red","iphone",2333);
var oPhone = new Phone("blue","huawei",2666);
//实质上,JS 这里仅仅是借用了关键字 new
原型方式,每个由构造器创建的对象拥有一个指向构造器 prototype 属性值的隐式引用,这个引用称之为原型。进一步,每个原型可以拥有指向自己原型的隐式引用 (proto),如此循环下去就是所谓的原型链。
function Phone(){}
Phone.prototype.color = "red";
Phone.prototype.brand = "iphone";
Phone.prototype.showColor = function(){console.log(this.color)}; //red
var p = new Phone();
//对象的隐式引用指向了构造器的 prototype 属性,所以此处打印 true
console.log( p.__proto__ === Phone.prototype );
//原型本身是一个 Object 对象,所以他的隐式引用指向了 Object 构造器的 prototype 属性 , 故而打印 true
console.log(Phone.prototype.__proto__ === Object.prototype );
// 构造器 Phone 本身是一个函数对象,所以此处打印 true
console.log( Phone.__proto__ === Function.prototype );
typeOf、valueof
typeof
对变量或值调用 typeof 运算符将返回以下值之一“undefined、boolean、number、string、object”。typeof 是对基本数据类型判断的最佳工具。
console.log(typeof undefined) //"undefined"
console.log(typeof true) //"boolean"
console.log(typeof 2) //"number"
console.log(typeof "string") //"string"
console.log(typeof null) //"object"
console.log(typeof {}) //"object"
JavaScript 中的每个对象都是 Object 对象的实例且继承它所有的属性和方法。
console.log(typeof Object) //"function" 返回了对象的 function 构造函数
console.log(typeof Boolean) //"function" 同上,我们可以证明
console.log(typeof Object === typeof Boolean) //true
//检查变量
var obj = new Object();
console.log(typeof obj) //"object" : 变量类型为引用类型
var boolean = new Boolean();
console.log(typeof boolean); //"object" : 变量类型为引用类型
console.log(Object.constructor === Boolean.constructor); //true
valueof
返回最适合该对象的原始值,该方法都属于 Object 对象的继承。对于许多对象,该方法返回的值都与 ToString() 的返回值相同。
var boolean = true;
console.log(boolean.valueOf()); //true
var arr = [];
console.log(arr.valueOf()) //[]
var num = 6;
console.log(num.valueOf()); //6
参考
https://www.ibm.com/developerworks/cn/web/1304_zengyz_jsoo/
http://www.w3school.com.cn/js/pro_js_object_oriented.asp
http://www.cnblogs.com/TomXu/archive/2012/02/06/2330609.html
跟 AI 学习 - JavaScript 面向对象
基本概念
- 面向对象是一种编程范式,主要思想是将数据和操作数据的方法打包在一起。
- 对象是一种复合值,可将其看作一个属性的集合。
- 对象的属性可以包含基本类型、函数或其他对象。
创建对象
工厂模式
- 通过工厂函数来创建对象,函数内部封装了具体的实现细节。
- 缺点:无法识别对象类型,即无法通过 instanceof 判断对象类型。
function createPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayHi = function() {
console.log(`Hi, my name is ${this.name}, I'm ${this.age} years old.`);
};
return obj;
}
var person1 = createPerson('Alice', 20);
var person2 = createPerson('Bob', 25);
person1.sayHi(); // Hi, my name is Alice, I'm 20 years old.
person2.sayHi(); // Hi, my name is Bob, I'm 25 years old.
构造函数模式
- 使用构造函数来创建对象,通过 new 关键字创建对象时,会自动执行构造函数内部的代码。
- 构造函数的命名约定:首字母大写,用于区分普通函数。
- 缺点:每个实例都有一份相同的方法,浪费内存。
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log(`Hi, my name is ${this.name}, I'm ${this.age} years old.`);
};
}
var person1 = new Person('Alice', 20);
var person2 = new Person('Bob', 25);
person1.sayHi(); // Hi, my name is Alice, I'm 20 years old.
person2.sayHi(); // Hi, my name is Bob, I'm 25 years old.
原型模式
- 将对象的共同方法和属性放在原型对象中,每个实例共享原型对象上的属性和方法。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function() {
console.log(`Hi, my name is ${this.name}, I'm ${this.age} years old.`);
};
var person1 = new Person('Alice', 20);
var person2 = new Person('Bob', 25);
person1.sayHi(); // Hi, my name is Alice, I'm 20 years old.
person2.sayHi(); // Hi, my name is Bob, I'm 25 years old.
组合模式
- 使用构造函数模式和原型模式的组合方式来创建对象。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function() {
console.log(`Hi, my name is ${this.name}, I'm ${this.age} years old.`);
};
var person1 = new Person('Alice', 20);
var person2 = new Person('Bob', 25);
person1.sayHi(); // Hi, my name is Alice, I'm 20 years old.
person2.sayHi(); // Hi, my name is Bob, I'm 25 years old.
继承
原型链继承
- 将子类的原型对象指向父类实例,从而实现继承。
- 缺点:所有子类实例共享同一个父类实例上的属性和方法,无法实现多继承。
function Animal() {
this.species = 'animal';
}
function Dog(name) {
this.name = name;
}
Dog.prototype = new Animal();
var dog1 = new Dog('小黄');
console.log(dog1.species); // animal
构造函数继承
- 在子类构造函数中调用父类构造函数,将父类的属性添加到子类实例上。
- 缺点:无法继承父类原型对象上的属性和方法。
function Animal(species) {
this.species = species;
}
function Dog(name, species) {
Animal.call(this, species);
this.name = name;
}
var dog1 = new Dog('小黄', 'dog');
console.log(dog1.species); // dog
组合继承
- 结合原型链继承和构造函数继承的优点。
- 子类实例既能够访问父类原型对象上的方法,又能够拥有自己的属性。
function Animal(species) {
this.species = species;
}
Animal.prototype.saySpecies = function() {
console.log(`I'm a ${this.species}.`);
};
function Dog(name, species) {
Animal.call(this, species);
this.name = name;
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
var dog1 = new Dog('小黄', 'dog');
console.log(dog1.species); // dog
dog1.saySpecies(); // I'm a dog.
原型式继承
- 借助已有的对象创建新的对象,实现对原有对象的一种增强或修正。
var parent = {
name: 'parent',
sayHi: function() {
console.log(`Hi, my name is ${this.name}.`);
}
};
var child = Object.create(parent);
child.name = 'child';
child.sayHi(); // Hi, my name is child.
寄生式继承
- 在原型式继承的基础上,增加了对返回对象的封装。
- 缺点:无法做到函数复用,每个实例都会单独创建方法。
function createChild(parent) {
var child = Object.create(parent);
child.sayHi = function() {
console.log(`Hi, my name is ${this.name}.`);
};
return child;
}
var parent = {
name: 'parent'
};
var child = createChild(parent);
child.name = 'child';
child.sayHi(); // Hi, my name is child.
寄生组合式继承
- 在组合继承的基础上,使用寄生式继承优化父类原型对象的创建。
function Animal(species) {
this.species = species;
}
Animal.prototype.saySpecies = function() {
console.log(`I'm a ${this.species}.`);
};
function Dog(name, species) {
Animal.call(this, species);
this.name = name;
}
Dog.prototype = Object.create(Animal.prototype, {
constructor: {
value: Dog,
enumerable: false,
writable: true,
configurable: true
}
});
var dog1 = new Dog('小黄', 'dog');
console.log(dog1.species); // dog
dog1.saySpecies(); // I'm a dog.