创建和操作

例如一个对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var mrPiao = {
name : '票',
age : 1,
sex : 'male',
health : 100,
eat : function() {
console.log('I am eating');
this.health --;
},
drink : function() {
console.log('I am drink');
this.health ++;
}
}

增删改查:

1
2
3
4
mrPiao.toy = '逗猫棒'; // 增
delete mrPiao.sex; // 删
mrPiao.age = 2; // 改
console.log(mrPiao.health); // 查

访问一个对象没有的属性时,会返回undefined

创建对象目前有三种方法:

  1. 对象字面量,又叫对象直接量(Plain Object),直接写即可。如 var object = {}
  2. 构造函数。构造函数又分为系统自带和自定义。
  3. Object.create(原型) 方法。

通过Object.create(原型)方法创建对象:

  1. 自己指定原型:var tuan = {...}
  2. 创建一个对象并将原型写入:var piao = Object.creat(tuan)
  3. 此时piao就为一个对象,它的原型是tuan
1
2
3
4
5
6
7
8
9
var tuan = {
name : '团',
age : 1
}

var piao = Object.create(tuan);

console.log(typeof(piao)); // object
console.log(piao.__proto__); // { name: '团', age: 1 }

构造函数

系统自带的构造函数

Object(); Array(); Number(); Boolean(); Date();

构造函数前面加一个new,就可以返回一个对象,然后通过变量接收即可。如:var obj = new Object();

构造函数创建的对象,和对象字面量创建的对象没有区别。构造函数就像一个工厂,每执行一次,就创建一个对象。每次创建的对象,一模一样但相互独立。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var num = new Number(123);

var str = new String('abcd');

var bol = new Boolean(true);

// 输出对象,可增加属性和方法

console.log(num); // Number {123}
console.log(str); // String {"abcd"}
console.log(bol); // Boolean {true}

num.name = '豆';
console.log(num); // Number {123, name: "豆"}

console.log(num * 2); // 246

自定义构造函数

由于构造函数和函数在结构上没有任何区别,所以为了区分,构造函数命名需要严格遵守大驼峰式命名规则:从第一个单词开始首字母大写。

依然使用new操作符生成对象。

具体步骤:

  1. 创建一个构造函数,里面写共有属性。
  2. 通过new返回一个对象,用变量接收。执行一次返回一个对象。
  3. 可以单独为这个对象增加属性,不影响返回的其他对象。
1
2
3
4
5
6
7
8
9
10
11
12
function Person() {
this.age = 3;
}

var person = new Person();
var person1 = new Person();

person.name = '团';

console.log(person);// Person { age: 3, name: '团' }

console.log(person1);// Person { age: 3}

内部原理

没有new操作符的话,就按照函数正常执行。

new操作符时,系统会执行如下三步:

  1. 在函数体最前面隐式的加上一个对象:this = Object.create(构造函数.prototype) = {};
  2. 执行 this.xxx = xxx;
  3. 隐式的返回 this:return this;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Student(name, age, sex) {

// var this = Object.create(Student.prototype) = {};

this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2019;

// return this;
}

var student = new Student('团', 3, 'female');

console.log(student); // Student {name: '团', age: 3, sex: 'female', grade: 2019}

使用

在构造函数内可以增加属性,还可以设置参数:

1
2
3
4
5
6
7
8
9
10
function Student(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2019;
}

var student = new Student('团', 3, 'female');

console.log(student); // Student {name: '团', age: 3, sex: 'female', grade: 2019}

如果对象的属性不存在,直接访问的话,会返回 “undefined”。

1
2
3
4
5
6
7
8
function Person() {
this.age = 3;
}

var obj = new Person();

console.log(obj); // Person { age: 3 }
console.log(obj.name); // undefined

在构造函数过程中,如果强制显式的返回一个引用值,可以将隐式返回的this覆盖。

1
2
3
4
5
6
7
8
9
10
11
function Person (name, height){
this.name = name;
this.height = height;
this.say = function () {
console.log(this.say);
}
return {a: 'b'};
}

var person = new Person('王', 180);
console.log(person); // { a: 'b' }

但如果是强制显式返回原始值,那么就无法覆盖,依然输出隐式的this

1
2
3
4
5
6
7
8
9
10
11
function Person (name, height){
this.name = name;
this.height = height;
this.say = function () {
console.log(this.say);
}
return 123;
}

var person = new Person('王', 180);
console.log(person); // Person {name: '王', height: 180, say: f}

包装类

前提

只有对象才有属性和方法,原始值不能有。

数字 number 包含原始值数字和数字对象。

  • 原始值数字不能有属性和方法。
1
2
3
var num = 123;
num.name = '数字';
console.log(num.name); // undefiend
  • 数字对象new Number(...)可以有属性和方法,且可以参与运算。不过运算完成后,它会变成原始值数字。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 数字对象:

var num = new Number(123);

num.name = '数字';

console.log(num); // Number{123, name: '数字'}

console.log(typeof num); // object

console.log(num * 2); // 246

console.log(typeof( num * 2)); // number

字符串 string 包含原始值字符串和字符串对象。

  • 原始值字符串不能有属性和方法。
1
2
3
var str = 'abcd';
str.name = '字符串';
console.log(str.name); // undefiend
  • 字符串对象new String(...)可以有属性和方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var str = new String('abcd');

str.name = '字符串';
str.sayValue = function () {
return this.name;
}

console.log(str); // String {"abcd", name: "字符串", sayValue: f}

console.log(typeof str); // object

var result = str.sayValue();

console.log(result); // 字符串

布尔 boolean 同上。

1
2
3
4
5
var bol = new Boolean(true);

bol.name = '布尔';

console.log(bol); // Boolean {true, name: "布尔"}

null 和 undefined 只有原始值,它们不会经过包装类,也就不会有对象,进而不会有原型,而对象的最终原型里的toString()方法也就无法使用。

1
2
3
null.name = '空'; // 报错:Uncaught TypeError: Cannot set property 'name' of null

undefined.name = '未定义'; // 报错:Uncaught TypeError: Cannot set property 'name' of undefined

原始值与包装类

原始值不能有属性和方法,但是往原始值上面加了属性后没有报错,是因为经历了包装类的过程:

  1. 第一步:原始值调用属性num.len = 3;,系统隐式地自动新增一个数字对象并赋值:new Number(num).len = 3;,紧接着将它删除:delete
  2. 第二步:当你访问num.len这个属性时,系统又重新建立一个数字对象:new Number(num).len给你访问。
  3. 结果:访问一个对象没有的属性时,会输出undefined
1
2
3
4
5
6
7
8
//包装类

var num = 4;

num.len = 3; // new Number(num).len = 3; delete

//new Number(num).len(这个 len 属性是自定义的,系统没有,所以访问不到)
console.log(num.len); // undefined

经历过包装类后,不会报错,但最后结果一定是undefined

例题

例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引用值
// 数组,通过 length 进行截断操作:

var arr = [1, 2, 3, 4, 5];
arr.length = 2;
console.log(arr); // [1, 2]

// 原始值
// 字符串的 length 是在系统自动新增的字符串对象上截断,紧接着就删除了,和原来的字符串没关系:

var str = 'abcd';
str.length = 2; // new String('abcd').length = 2; delete

console.log(str); // abcd

// new String('abcd').length
console.log(str.length); // 4 (字符串对象自带 length 属性,可以访问但截断的不是它)

综合考察:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 闭包和构造函数
function Person(name, age, sex){
// var this = {};
var a = 0;
this.name = name;
this.age = age;
this.sex = sex;
function sss() {
a ++;
console.log(a);
}
this.say = sss;

// return this;

// 函数 sss 是对象 this 的属性,因为隐式的 return this,所以会被返回出去,由此它与构造函数 Person 形成闭包
}

var oPerson = new Person();
oPerson.say(); // 1
oPerson.say(); // 2

// 重新执行一次构造函数 Person,生成新的执行期上下文,和第一次的作用域链没关系
// 产生新的对象,新的闭包

var oPerson1 = new Person();
oPerson1.say(); // 1