JavaScript对象机制
作者:互联网
JavaScript对象机制
ECMAScript规范明确定义了这样的概念:对象是零到多个的属性的集合。JavaScript是基于原型编程,ES5中使用了很多特性来模拟类的行为,虽然ES6新引入了class
关键字正式定义类,但本质仍是原型和构造函数。
一、创建对象的方式
JavaScript提供了多种创建对象的方式:
1.工厂模式
function createPerson(first, last, age, gender, interests) {
let o = new Object();
o.name = {
first,
last
};
o.age = age;
o.gender = gender;
o.interests = interests;
o.greeting = function () {
alert('Hi I\'m' + o.name.first + '.');
}
}
但是工厂模式存在一个问题,即无法表示新创建的对象是什么类型(所有创建出的对象都是Object,不具有唯一性)。
2.构造函数模式
如果自定义构造函数,就能够以函数的形式为自己的对象类型定义属性和方法。
function Person(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
this.greeting = function () {
alert('Hi, I\'m' + o.name.first + '.');
}
}
let person1 = new Person('Tammi', 'Smith', 17, 'female', ['music', 'skiing', 'kickboxing']);
由于构造函数就是能创建对象的函数,因此为了区分,构造函数首字母为大写,非构造函数首字母为小写。事实上该函数是以类的身份来声明了一系列的属性。创建Person的实例,需要使用new操作符。使用构造函数模式会带来如下操作:
- 内存中创建新对象
- 新对象[[Prototype]]特性被赋值为构造函数的Prototype特性
- this指向这个新对象
- 函数可以返回非空对象。如果没有则返回创建的新对象
构造函数也是普通的函数,因此可以将其作为普通函数调用。没有使用new
操作符时,结果会将属性和方法添加到Global对象上。而在浏览器中Global对象为window对象。
// 作为函数调用
Person('Tammi', 'Smith', 17, 'female', ['music', 'skiing', 'kickboxing']);
console.log(window.age); // 17
// 在另一个对象的作用域中调用
// 通过Call()调用时,将对象o指定为Person()内部的this值,因此执行完函数后所有属性和方法将添加到对象o上
let o = new Object();
Person.call(o, 'Tammi', 'Smith', 17, 'female', ['music', 'skiing', 'kickboxing']);
console.log(o.first.name); // Tammi
使用构造函数模式时存在的问题是会定义两个不同的Function
实例。不同实例上的函数同名却不等价。解决这个问题需要将函数定义转到构造函数外部:
function Person(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
this.greeting = greeting;
}
function greeting () {
alert('Hi, I\'m' + this.name.first + '.');
}
在构造函数内greeting
属性指向了全局的greeting()
函数。如此以来person1
和person2
共享了全局作用域上的greeting()
函数。但这样带来的问题也显而易见,如果该对象需要多个方法就要在全局作用域中定义多个函数。从面向对象编程的观点来看,这样的做法破坏掉了“封装性”。原型模式可以解决这一问题。
3.原型模式
使用原型对象的好处是,定义的所有属性和方法都可以被所有的对象实例共享。
function Person() {}
Person.prototype.name = ['Tammi', 'Smith'];
Person.prototype.age = 17;
Person.prototype.gender = 'female';
Person.prototype.interests = ['music', 'skiing', 'kickboxing'];
Person.prototype.greeting = function () {
alert('Hi, I\'m' + this.name.first + '.');
}
let person1 = new Person();
console.log(person1.age); // 17
let person2 = new Person();
console.log(person2.age); // 17
因此,person1和person2访问的都是相同属性和相同的greeting()
函数。
二、类
### 1.类的定义及构造函数
ES6引入了class
关键字来具备正式的定义类的能力。表面上看可以正式支持面向对象编程,但实际上仍是使用原型和构造函数的概念。constructor
关键字用于在类定义块内部创建类的构造函数,并告诉解释器在使用new
操作符创建类的新的实例时应调用这个函数。
class Person {
constructor(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
}
greeting() {
console.log(`Hi! I'm ${this.name.first}`);
};
farewell() {
console.log(`${this.name.first} has left the building. Bye for now!`);
};
}
2.实例化
实例化过程与构造函数模式相似:
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
han.greeting();
// Hi! I'm Han
let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
leia.farewell();
// Leia has left the building. Bye for now
事实上在后台这些类也将被转为原型模式。但ES6提供了更为便利的语法糖,降低了编写的难度。
class Person {
constructor() {
this.name = new String('Hellcat');
this.greeting = () => console.log(`Hi! I'm ${this.name}`);
this.interests = ['music', 'programming'];
}
}
而每一个实例都对应一个唯一的成员对象,这意味着所有成员都不会在原型上共享:
let p1 = new Person(), p2 = new Person();
p1.name === p2.name; // false
p1.greeting(); // Hi! I'm Hellcat
p2.greeting(); // Hi! I'm Hellcat
三、继承
面向对象的语言(如Java)提供两种继承:接口继承和实现继承。前者只继承方法的函数签名,后者继承实际的方法。但在ECMAScript中没有函数签名这一概念,因此无法实现接口继承。ECMAScript唯一支持的继承方式是实现继承,主要通过原型链来实现。
1. 原型链
每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。
2.盗用构造函数
在子类构造函数中调用父类构造函数。
3.基于类的继承
标签:name,对象,age,JavaScript,greeting,Person,机制,first,构造函数 来源: https://www.cnblogs.com/tedukuri/p/14396754.html