JS中的对象定义为:无序属性的结合,其属性可以包含基本值、对象或者函数
 
1、定义对象的方式
 
(1)、Object构造函数
var student = new Object();
student.name = 'Jim Green';
student.gender = 'Male';
student.age = 8;
student.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old`);
} student.say(); // My name is Jim Green, I'm 8 years old

(2)、对象字面量

var student = {
name: 'Jim Green',
gender: 'Male',
age: 8,
say: function() {
console.log(`My name is ${this.name}`)
}
} student.say(); // My name is Jim Green

2、属性类型

Object.defineProperty()方法:用于修改对象属性的默认特性
这个方法接收三个参数:属性所在的对象,属性的名字和一个描述符对象。其中,描述符对象必须是以下两种之一(不能同时是两者)
 
2-1、数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值
 
configurable:默认值为true。表示能否通过delete删除该属性,能否修改属性的特性,或者能否把属性修改为访问器属性
enumerable:默认值为true。表示能否通过 for-in 循环返回属性
writable:默认值为true。表示能否修改属性值
value:默认值为undefined。包含这个属性的属性值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置
var person = {
age: 8,
gender: 'male'
};
Object.defineProperty(person, "name", {
enumerable: false,
value: "Roger"
});
for(var key in person) {
console.log(`key --> ${key}, value --> ${person[key]}`);
} // key --> age, value --> 8
// key --> gender, value --> male
如果将name属性的configurable值改为false,并尝试删除该属性,非严格模式下无效,delete操作被忽略;严格模式下报错
// "use strict";
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
writable: true,
value: "Roger"
});
console.log(person.name); // Roger
delete person.name;
console.log(person.name); // Roger (configurable属性为false时,返回undefined) // Uncaught TypeError: Cannot delete property 'name' of #<Object> (严格模式下报错)
 
通过修改name属性的writable将其变为只读属性,非严格模式下修改该属性无效,赋值操作被忽略;严格模式下会报错
// "use strict"
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Roger"
}) console.log(person.name); // Roger
person.name = "Frank";
console.log(person.name); // Roger // console.log(person.name); // Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
注意:
(1)、调用Object.defineProperty()方法时,如果不指定,configurable、enumerable和writable特性的默认值就都是false
var person = {};
Object.defineProperty(person, "name", {
configurable: true,
value: "Roger"
});
person.name = "Frank";
console.log(person.name); // Roger Object.defineProperty(person, "name", {
writable: true,
value: "Roger"
}); person.name = "Kobe";
console.log(person.name); // Kobe
(2)、一旦把属性定义为不可配置的(configurable为false),就不能再把它设为可配置了。此时,再修改除 writable之外的特性,都会导致报错
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Roger"
});
person.name = "Frank";
console.log(person.name); // Roger Object.defineProperty(person, "name", { // Uncaught TypeError: Cannot redefine property: name
writable: true,
value: "Roger"
}); person.name = "Kobe";
console.log(person.name);

2-2、访问器属性

访问器属性不包含数据值,它们包含一对getter和setter函数(非必需)。共包含4个特性:

 
configurable:默认值为true;表示能否通过delete删除属性,能否修改属性的特性,或者能否把属性修改为数据属性
enumerable:默认值为true;表示能否通过for-in循环此属性
get:默认值为undefined;在读取属性时调用函数
set:默认值为undefined;在写入属性时调用函数
var person = {
name: 'Kobe',
_number: 8
} Object.defineProperty(person, 'number', {
get: function() {
return this._number;
},
set: function(newValue) {
if(newValue > 8) {
this._number = 24;
}
}
}) person.number = 9;
console.log(person._number); //
Object.defineProperties() 方法:可以通过描述符一次定义多个属性,这个方法接收两个对象参数
 
第一个对象是要添加和修改其属性的对象
第二个对象与第一个对象中要添加或修改的属性一一对应
var book = {};
Object.defineProperties(book, {
_year: {
writable: true,
value: 8
},
year: {
get: function() {
return this._year;
},
set: function(newValue) {
if(newValue > 8) {
alert('111')
this._year = 24;
}
}
}
}) console.log(book); // {_year: 8}
book.year = 9;
console.log(book); // {_year: 24}
Object.getOwnPropertyDescriptor() 方法用于读取给定属性的描述符,这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称,返回值是一个对象。
 
如果是访问器属性,这个对象包含 configurable, enumerable, get, set;
如果是数据属性,这个对象包含 configurable,enumerable,writeable,value;
var book = {};
Object.defineProperties(book, {
_year: {
writable: true,
value: 8
},
year: {
get: function() {
return this._year;
},
set: function(newValue) {
if(newValue > 8) {
alert('111')
this._year = 24;
}
}
}
}); var obj1 = Object.getOwnPropertyDescriptor(book, '_year');
var obj2 = Object.getOwnPropertyDescriptor(book, 'year'); console.log(obj1); // {value: 8, writable: true, enumerable: false, configurable: false}
console.log(obj2); // {get: ƒ, set: ƒ, enumerable: false, configurable: false}
3、创建对象
 
面向对象的编程语言都有类的概念,通过类可以创建任意多个具有相同属性和方法的对象
// 例如:Java中定义一个Student类
public class Student { private String name; // 姓名
private int age; // 年龄 public Student(String name, int age) { //构造器
super();
this.name = name;
this.age = age;
} // 属性的getter和setter方法
public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} // 自定义方法
public void say() {
System.out.println("姓名:" + this.name + "年龄:" + this.age);
} } // 通过Student类来创建实例对象
Student xiaoMing = new Student("XiaoMing", 12);
Student xiaoHong = new Student("XiaoHong", 9); xiaoMing.say(); // 姓名:XiaoMing年龄:12
xiaoHong.say(); // 姓名:XiaoHong年龄:9
如果是通过JS来创建两个学生对象,可以用对象字面量或者Object构造函数创建单个的对象:
var xiaoMing = {
name: "XiaoMing",
age: 12
}; var XiaoHong = {
name: "XiaoHong",
age: 9
}
这么做有两个明显的弊端,一是重复的代码太多,如果一个班级有60名学生,就要重复60次;二是可读性差,只知道是个对象,到底是个什么对象,学生的集合,人的集合还是其它?
 
3-1、工厂模式
 
用函数来封装以特定接口创建对象的细节
function createStudent(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
};
return obj;
} var xiaoMing = createStudent('XiaoMing', 12);
var xiaoHong = createStudent('XiaoHong', 8); xiaoMing.say(); // My name is XiaoMing, I'm 12 years old.
xiaoHong.say(); // My name is XiaoHong, I'm 8 years old.
弊端:这样做虽然很好的解决了代码重复的问题,但还是不知道创建的到底是个什么类型的对象
 
3-2、构造函数模式
 
使用自定义构造函数来定义对象类型的属性和方法,通常为了区别于普通函数,会将构造函数的首字母大写
function Student(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
};
} var xiaoMing = new Student('XiaoMing', 12);
var xiaoHong = new Student('XiaoHong', 8); xiaoMing.say(); // My name is XiaoMing, I'm 12 years old.
xiaoHong.say(); // My name is XiaoHong, I'm 8 years old.
与工厂模式相比,有以下几个不同的地方:
-、没有显示的创建对象
-、直接将属性和方法赋给了this对象
-、没有return语句
使用new操作符创建自定义对象,主要经历了4个步骤:
-、创建一个新对象
-、将this指向新对象
-、为新对象添加属性
-、返回新对象
对象都有一个constructor属性用于标识对象的类型,该属性指向创建对象的构造函数
console.log(xiaoMing.constructor)

// ƒ Student(name, age) {
// this.name = name;
// this.age = age;
// this.say = function() {
// console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
// };
// } console.log(xiaoMing.constructor == Student) // true
console.log(xiaoMing.constructor == xiaoHong.constructor) // true
console.log(xiaoMing instanceof Student) // true
console.log(xiaoMing instanceof Object) // true
构造函数与普通函数的唯一区别,就在于调用方式。任何函数,只要使用new操作符来调用就可以作为构造函数,如果不通过new操作符来调用,那就是普通函数
function Student(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
};
} // 将Student当作构造函数(this指向实例对象)
var xiaoMing = new Student('XiaoMing', 12);
xiaoMing.say(); // My name is XiaoMing, I'm 12 years old. // 将Student当作普通函数(由于Student函数属于全局作用域,因此实际上是window.Student(),this指向window)
Student('Bob', 9);
say(); // My name is Bob, I'm 9 years old. // 在特定的作用域中调用函数(this指向obj)
var obj = new Object();
Student.call(obj, 'Ryan', 30);
console.log(obj); // {name: "Ryan", age: 30, say: ƒ}
obj.say(); // My name is Ryan, I'm 30 years old.
 
弊端:由于方法也是对象(如果对象的属性是一个函数就称之为方法),这就意味着每次实例化一个对象,都重新创建了一个对象
// 上面的say方法等价于
function Student(name, age) {
this.name = name;
this.age = age;
this.say = new Function(`My name is ${this.name}, I'm ${this.age} years old.`);
}
为了解决这个问题,似乎可以把方法提出来
function Student(name, age) {
this.name = name;
this.age = age;
this.say = say;
}
function say() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
} var xiaoHong = new Student('XiaoHong', 8);
xiaoHong.say(); // My name is XiaoHong, I'm 8 years old.

这样做的话同样存在问题,首先是封装性不太好,对象的某些属性必须依赖于全局的属性;其次,我们期望全局作用域内的函数say只能用于构造函数Student,这样就跟js的理念相冲突了

3-3、原型模式

每个函数都有一个prototype属性,prototype属性是一个指针,指向函数的原型对象,该对象包含所有实例对象共享的属性和方法
function Student() {

}
Student.prototype.name = 'Bob';
Student.prototype.age = 12;
Student.prototype.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
} var xiaoMing = new Student();
var xiaoHong = new Student(); xiaoMing.say(); // My name is Bob, I'm 12 years old.
xiaoHong.say(); // My name is Bob, I'm 12 years old.
与构造模式不同的时,所有实例对象访问的都是相同的属性和同一个say方法
对比prototype和constructor
prototype:每个函数都有一个 prototype属性,指向一个原型对象
constructor:每个对象都有一个 constructor属性,指向创建该对象的构造函数
通过构造函数创建的实例对象内部都包含一个指针(内部属性),指向构造函数的原型对象,这个指针就是 [[Prototype]]。换句话说,实例对象与构造函数没有直接关系
但是,脚本中没有标准的方式访问[[Prototype]],一些现代浏览器中可以通过__proto__属性来访问
console.log(xiaoMing.__proto__);      // {name: "Bob", age: 12, say: ƒ, constructor: ƒ}
console.log(xiaoMing.__proto__ === Student.prototype); // true
__proto__属性是一个访问器属性,其中包含了getter(读取器)和 setter(设置器)
getter:暴露了一个对象的内部[[Prototype]],这个值就是构造器函数的prototype属性,如:Array.prototype, Object.prototype
setter:允许对象的[[Prototype]]被更改
虽然无法直接访问 [[Prototype]] 属性,但可以通过 isPrototypeOf() 方法来测试一个对象是否存在于另一个对象的原型链上
prototypeObj.isPrototypeOf(object)
参数:在object对象的原型链上搜寻
返回值:Boolean
console.log(Student.prototype.isPrototypeOf(xiaoMing));   // true
console.log(Student.prototype.isPrototypeOf(xiaoHong)); // true
// 说明实例对象 xiaoMing和xiaoHong都存在于Student.prototype的原型链上
尽管部分现代浏览器都实现了__proto__属性,但是该属性从未被包含在ECMA规范中,因此不推荐使用。
从ES5开始,[[Prototype]] 可以通过 Object.getPrototypeOf() 访问器访问,用于返回指定对象的原型
console.log(Object.getPrototypeOf(xiaoHong));  // {name: "Bob", age: 12, say: ƒ, constructor: ƒ}
console.log(Object.getPrototypeOf(xiaoHong) === Student.prototype); // true
当为某个实例对象添加同名属性时,这个属性就会屏蔽掉原型对象中保存的同名属性,也就是说,如果实例对象自己有某个属性,就不会去它的原型对象上找
xiaoMing.name = 'XiaoMing';

console.log(xiaoMing.name);   // XiaoMing
console.log(xiaoHong.name); // Bob

为了进一步对比,可以删除掉实例对象 xiaoMing 的属性name,然后再访问该属性

delete xiaoMing.name;

console.log(xiaoMing.name);   // Bob
console.log(xiaoHong.name); // Bob
当前测试的是属性值是基本类型的情况,如果属性值是引用类型就不再屏蔽了
function Student() {

}
Student.prototype = {
name: 'Bob',
age: 12,
course: ['Chinese', 'Math']
} var xiaoMing = new Student();
var xiaoHong = new Student(); xiaoMing.name = 'XiaoMing';
console.log(xiaoMing.name); // XiaoMing
console.log(xiaoHong.name); // Bob xiaoMing.course.push('English');
console.log(xiaoMing.course); // ["Chinese", "Math", "English"]
console.log(xiaoHong.course); // ["Chinese", "Math", "English"]
如果需要判断一个属性是存在于实例对象中,还是原型对象中,可以使用 obj.hasOwnProperty(prop) 方法
xiaoMing.name = 'XiaoMing';

console.log(xiaoMing.hasOwnProperty('name'));   // true     --- 来自实例
console.log(xiaoHong.hasOwnProperty('name')); // false --- 来自原型
// 实例对象xiaoMing有自己的name属性,xiaoHong则没有
in操作符:只要通过对象能够访问到给定的属性(不管是对象自身的属性还是原型对象的属性),都返回true
hasProperty():只有属性存在于对象时才返回true
xiaoMing.name = 'XiaoMing';

console.log('name' in xiaoMing);                // true
console.log(xiaoMing.hasOwnProperty('name')); // true console.log('name' in xiaoHong); // true
console.log(xiaoHong.hasOwnProperty('name')); // false
因此,要判断一个属性是原型中的属性,只需要同时满足in返回true,hasOwnProperty()返回false即可
xiaoMing.name = 'XiaoMing';

// 判断一个属性仅存在于对象的原型中
function checkPropertyInPrototype(Object, prop) {
return (prop in Object) && !Object.hasOwnProperty(prop)
} console.log(checkPropertyInPrototype(xiaoMing, 'name')); // false
console.log(checkPropertyInPrototype(xiaoHong, 'name')); // true
for-in 循环不仅会遍历对象自身的属性,还会遍历对象原型的属性(这里说的属性必须是可枚举的)
function Student() {

}
Student.prototype.name = 'Bob';
Student.prototype.age = 12;
Student.prototype.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
} var xiaoHong = new Student();
xiaoHong.gender = 'female'; for(var prop in xiaoHong) {
console.log(prop +' --> '+ xiaoHong[prop]);
} /*
gender --> female
name --> Bob
age --> 12
say --> function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
}
*/
弊端:所有实例只能共享属性,无法通过构造函数初始化参数
 
3-4、组合构造函数模式和原型模式
 
这也是创建自定义类型的最常见方式,构造函数模式用于定义实例属性,原型模式用于定义方法和共有的属性
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
say: function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old.`);
}
} var xiaoMing = new Person('XiaoMing', 12);
var xiaoHong = new Person('xiaoHong', 9); xiaoMing.say(); // My name is XiaoMing, I'm 12 years old.
xiaoHong.say(); // My name is xiaoHong, I'm 9 years old.

4、继承

JS中的继承主要是依靠原型链来实现的

4-1、原型链

简单回顾下构造函数、原型和实例对象的关系

(1)、每个构造函数都包含一个prototype属性指向原型对象

(2)、每个原型对象都包含一个constructor指针指向构造函数

(3)、每个实例都包含一个[[prototype]]指针指向构造函数的原型对象

原型链的基本思想就是利用原型,让一个引用类型继承另一个引用类型的属性和方法

具体说就是让原型对象等于另一个类型的实例,由于该实例包含指向其原型对象的指针,因此,此时的原型对象也包含了另一个原型对象的 指针。层层向上直到一个对象的原型对象是null。根据定义,null没有原型,并作为原型链中的最后一个环节

function Person() {

  }
Person.prototype = {
leg: 4,
ear: 2
} function Student() { } // 让Student的原型对象等于Person的实例,因此Student的原型对象也包含了指向Person的原型对象的指针
Student.prototype = new Person(); // 给Student的原型对象添加方法
Student.prototype.say = function() {
console.log(`I have ${this.ear} ears and ${this.leg} legs.`);
} var student = new Student();
student.say(); // I have 2 ears and 4 legs.
console.log(student.toString()); // [object Object] console.log(Object.getPrototypeOf(student) === Student.prototype); // true (student实例的原型对象就是Student.prototype)
console.log(Person.prototype.isPrototypeOf(student)); // true (student实例存在于Person对象的原型链上)
console.log(student instanceof Person); // true (student就是Person对象的实例)
console.log(student.hello()); // Uncaught TypeError: student.hello is not a function

图中所示,红色的粗线条代表的就是原型链,绿色细线条代表是构造函数与其原型对象之间的关联。

执行 student.say(),student实例自己没有say方法,于是沿着原型链在它的原型对象是找到了,但是该原型对象中并没有ear和leg属性,由于该原型对象包含了指向Person原型对象的指针,因此,继续沿着原型链向上查找,在Person的原型对象上找到了ear和leg属性

执行 student.toString(),student实例没有提供该方法,于是沿着原型链逐级向上查找,由于所有引用类型都继承自Object,最终在Object的原型对象上找到了

执行 student.hello(),student实例没有提供该方法,沿着原型链逐级向上查找,都没有找到该方法,因此会报错

注意:

(1)、通过原型链实现继承时,不能使用对象字面量创建原型方法,否则会重写原型链

function Person() {

}
Person.prototype = {
say: function() {
console.log('hello world');
}
} function Student() { } // 让Student的原型对象等于Person的实例,因此Student的原型对象也包含了指向Person的原型对象的指针
Student.prototype = new Person(); // 使用对象字面量给Student创建原型方法
Student.prototype = {
class: 2,
grade: 1
} var student = new Student();
student.say(); // Uncaught TypeError: student.say is not a function

此例中,先是把Person的实例赋值给Student的原型对象,构建出了一条图中红色粗线部分显示的原型链。然后,使用对象字面量的方法使得Student的原型对象指向了一个实例对象,切断了原来的原型链,重新构建出图中蓝色粗线部分的原型链,自然就找不到say方法了

(2)、给原型添加方法的代码一定要放在替换原型的语句之后。如子类型重写父类型中的某个方法,或在子类型中添加一个父类型中不存在的方法

function Person() {

}
Person.prototype = {
say: function() {
console.log('hello world');
}
} function Student() { } // 让Student继承Person
Student.prototype = new Person(); // 子类型重写父类型中的方法
Student.prototype.say = function() {
console.log(`I'm Iron man`)
} // 子类型中添加一个父类型中不存在的方法
Student.prototype.walk = function() {
console.log('walk with legs')
} var student = new Student(); student.say(); // I'm Iron man
student.walk(); // walk with legs

首先确定了Student继承Person这一继承关系,Student原型对象可以读取到Person原型对象中的say方法,然后为Student原型对象添加的重写和新方法,会覆盖掉原来的say方法

function Person() {

}
Person.prototype = {
say: function() {
console.log('hello world');
}
} function Student() { } // 子类型重写父类型中的方法
Student.prototype.say = function() {
console.log(`I'm Iron man`)
} // 子类型中添加一个父类型中不存在的方法
Student.prototype.walk = function() {
console.log('walk with legs')
} // 让Student继承Person
Student.prototype = new Person(); var student = new Student(); console.log(Person.prototype.isPrototypeOf(student)); // true
student.say(); // hello world
student.walk(); // Uncaught TypeError: student.walk is not a function

与前面唯一的区别就是继承关系是在Student原型对象添加方法之后确定的,尽管student实例和Person的原型对象依然在同一条原型链上,但是会用Person原型对象中的属性和方法覆盖掉Student原型对象中的属性和方法,导致输出和前面的不一样

原型链弊端:
(1)、针对属性值是引用类型的情况,当某一个实例对象改变该共享属性时,其它实例也会随之改变

function Person() {
this.course = ['chinese', 'math'];
}
function Student() { }
Student.prototype = new Person(); var student1 = new Student(); console.log(student1.course); // ["chinese", "math"] student1.course.push('english');
var student2 = new Student();
console.log(student2.course); // ["chinese", "math", "english"]

(2)、创建子类型的实例时,不能向父类型的构造函数中传递参数。实际上就是一旦给父类型的构造函数传递参数,就会影响所有的实例对象

function Person(name, age) {
this.name = name;
this.age = age;
}
function Student() { }
// 此处调用父类型的构造函数是需要传参的
Student.prototype = new Person('Bob', 12); var s1 = new Student();
var s2 = new Student(); console.log(s1.name); // Bob
console.log(s2.name); // Bob

4-2、借用构造函数

用于解决原型中包含引用类型值所带来的问题

基本思想是在子类型构造函数中调用父类型的构造 函数,通过call()或apply()方法在(将来)新创建的对象上执行构造函数

function Person(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old`)
}
} function Student(name, age, grade) {
Person.call(this, name, age);
this.grade = grade;
}
function Teacher(name, age, height) {
Person.apply(this, [name, age]);
this.height = height;
} var student = new Student("Jim", 9, 4);
var teacher = new Teacher('Mr Lee', 33, 1.75); student.say(); // My name is Jim, I'm 9 years old
teacher.say(); // My name is Mr Lee, I'm 33 years old

通过这种方式,子类型不仅可以继承父类型,还可以向父类型构造函数传递参数

如何解决原型中包含引用类型值带来的问题?

function Person() {
this.course = ['chinese', 'math'];
}
function Student() {
Person.call(this);
} var s1 = new Student(); console.log(s1.course); // ["chinese", "math"]
s1.course.push('english');
console.log(s1.course); // ["chinese", "math", "english"] var s2 = new Student();
console.log(s2.course); // ["chinese", "math"]

利用call方法将this绑定到了实例对象,所以即便修改了引用类型的值,也只是在实例对象的作用域范围内,不影响其他实例

弊端:方法都在构造函数中定义,如果在父类型的原型对象中定义方法,对子类型还是不可见的

function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {
console.log(`My name is ${this.name}`);
} function Student(name, age) {
Person.call(this, name, age);
} var s = new Student("Jim", 2);
s.say(); // Uncaught TypeError: s.say is not a function

4-3、组合继承

就是将原型链和借用构造函数两种方法组合在一起实现继承,这也是JS中最常用的继承模式

思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承

function Person(name) {
this.name = name;
this.course = ['chinese', 'math'];
}
Person.prototype.say = function() {
console.log(`My name is ${this.name}`);
} function Student(name, age) {
Person.call(this, name); // 继承实例属性
this.age = age;
} // 继承原型属性和方法
Student.prototype = new Person(); // 添加子类方法
Student.prototype.sayAge = function() {
console.log(`I'm ${this.age} years old`);
} // 重写父类方法
Student.prototype.say = function() {
console.log(`My name is ${this.name}, I'm ${this.age} years old`);
} var s1 = new Student('Bob', 8); // 修改引用类型值的属性
s1.course.push('english'); s1.sayAge(); // I'm 8 years old
s1.say(); // My name is Bob, I'm 8 years old
console.log(s1.course); // ["chinese", "math", "english"] var s2 = new Student('Jim', 11);
console.log(s2.course); // ["chinese", "math"]

进击JavaScript核心 --- (3)面向对象的更多相关文章

  1. 进击JavaScript核心 --- (1)基本数据类型

    ES5之前提供了 5种基本数据类型 和 1种引用数据类型 基本数据类型:Undefined, Null, String, Number, Boolean 引用数据类型:Object ES6开始引入了一 ...

  2. 进击JavaScript核心 --- (2)函数和预解析机制

    一.函数 每个函数都是 Function类型的实例,也具有属性和方法.由于函数也是一个对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定 1.函数的定义方式 (1).函数声明 fun ...

  3. javascript 核心语言笔记- 3 - 类型、值和变量

    JavaScript 中的数据类型分为两类:原始类型(primitive type)和对象类型(object type).原始类型包括数字.字符串和布尔值 JavaScript 中有两个特殊的原始值: ...

  4. JavaScript 核心参考教程 内置对象

    这个标准基于 JavaScript (Netscape) 和 JScript (Microsoft).Netscape (Navigator 2.0) 的 Brendan Eich 发明了这门语言,从 ...

  5. JavaScript 核心学习——继承

    本篇博文讲述如何在 JavaScript 中实现继承,以及原型与原型链的知识,在附录中将会讲述 JavaScript 面向对象的常见错误. ##原型与原型链在 JavaScript 中,使用类将会付出 ...

  6. 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

    前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...

  7. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  8. 前端开发:面向对象与javascript中的面向对象实现(一)

    前端开发:面向对象与javascript中的面向对象实现(一) 前言: 人生在世,这找不到对象是万万不行的.咱们生活中,找不到对象要挨骂,代码里也一样.朋友问我说:“嘿,在干嘛呢......”,我:“ ...

  9. 最新的JavaScript核心语言标准——ES6,彻底改变你编写JS代码的方式!【转载+整理】

    原文地址 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和for-of循环 生成器 Generators 模板字符串 不定参数和默认参数 解构 Destructu ...

随机推荐

  1. Jmeter之TCP取样器(模拟数据上报压测)

    TCP压测 场景:模拟硬件设备上报数据(登录,心跳,GPS定位数据/光感数据/电量数据),对这个功能进行压测 啰嗦一句:TCP压测很简单,只要调通了一个TCP,后续的逻辑判断就用逻辑控制器和正则处理就 ...

  2. 怎样理解 Vue 项目的目录结构?

      Vue 项目的目录结构如下, 我们将会在后面逐个去了解它们的作用: 01. build - 存储项目构建相关的代码, 比如 webpack. 02. config - Vue 的配置目录,包括端口 ...

  3. C# WebApi日期格式化

    WebApi中日期格式化:在WebApiConfig文件中加入如下代码即可,之前遇到的问题,日期中总带有T,现在记录一下解决的方法. 代码: private static void ReturnDat ...

  4. centos配置发送邮件

    邮件已经可以接收到了 CentOS下发送邮件有很多种方法,这里采用比较简单的mail客户端,配置第三方邮件服务商,例如:163.com 1.安装所用软件 yum install mailx sendm ...

  5. 【Zabbix】分布式监控系统Zabbix【二】

    一.Zabbix基本操作 1.主机群组.主机.模板.触发器 a.创建主机群组和主机的过程比较简单,不再介绍 b.配置模板: 创建一个模板,将其分组到Template组,添加配置应用: 给应用创建监控项 ...

  6. Java学习笔记【十二、网络编程】

    原计划的学习结束时间是3月4日,目前看来已经延迟了,距离低标还差一些,多方面原因,也不找借口,利用周末赶赶进度,争取本周末把低标完成吧! 参考: http://www.runoob.com/java/ ...

  7. 【异常】553 Mail from must equal authorized user

    1 详细异常打印 2019-08-12 14:54:42,178 ERROR org.apache.camel.processor.DefaultErrorHandler: Failed delive ...

  8. servlet遇到的问题

    1 创建web项目没有xml自动生成 2  servlet 忽然报奇怪500错误  出现的BUG原因 JAVA bean没有设置  自动导入了其他User包

  9. Hadoop_04_Hadoop 的HDFS客户端shell命令

    1.Hdfs shell客户端命令操作: 1.1.查看命令列表:hadoop fs 帮助如下: Usage: hadoop fs [generic options] [-appendToFile &l ...

  10. C++之旋转矩阵和打印一个有规律的矩阵

    旋转数组 描述: 某个图像通过一个整数组成的m*n矩阵表示,其中每个整数表示一个像素值.写出一种方法,根据flag变量的值将图像向右或者向左旋转90°.如果flag值为0,则向左旋转,如果flag为1 ...