跳到主要内容

对象

1、对象创建方式

1.1、调用系统函数创建对象

(创建对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法。)

var obj = new Object();
obj.name = "Daotin";
obj.age = 18;
obj.eat = function () {
  console.log("我很能吃");
);

缺点:使用同一个接口创建很多对象,会产生大量的重复代码。

1.2、自定义构造函数创建对象

工厂模式创建对象:考虑到在 ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节(把创建一个函数的过程封装在一个函数里面),缺点:创建的对象属性都是一样的。

function creatObject() {
var obj = new Object();
obj.name = "Daotin";
obj.age = 18;
obj.eat = function () {
console.log("我很能吃");
};
return obj;
}

var person = creatObject();
person.eat();

工厂模式创建对象进阶版:可以修改属性

function creatObject(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.eat = function () {
console.log("我很能吃");
};
return obj;
}java

var person = creatObject("Daotin", 18);
person.eat();

自定义构造函数:(函数和构造函数的区别:没有区别,但是通常构造函数首字母大写

特点:

  • 没有显式地创建对象;
  • 直接将属性和方法赋给了 this 对象;
  • 没有 return 语句。
function CreatObject() { // 首字母大写
this.name = "Daotin";
this.age = 18;
this.eat = function () {
console.log("我很能吃");
};
}

var person = new CreatObject();
person.eat();

进阶(传参数):

function CreatObject(name, age) {
this.name = name;
this.age = age;
this.eat = function () {
console.log("我很能吃");
};
}

var person = new CreatObject();
person.eat();

构造函数的问题

构造函数模式虽然好用,但也并非没有缺点。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。在前面的例子中, person1 和 person2 都有一个名为 sayName()的方法,但那两个方法不是同一个 Function 的实例。不要忘了——ECMAScript 中的函数是对象,因此每定义一个函数,也就是实例化了一个对象。从逻辑角度讲,此时的构造函数也可以这样义。

function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function("alert(this.name)"); // 与声明函数在逻辑上是等价的
}

从这个角度上来看构造函数,更容易明白每个 Person 实例都包含一个不同的 Function 实例(以显示 name 属性)的本质。说明白些,以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建 Function 新实例的机制仍然是相同的。因此,不同实例上的同名函数是不相等的,以下代码可以证明这一点:alert(person1.sayName == person2.sayName); //false

然而,创建两个完成同样任务的 Function 实例的确没有必要;况且有 this 对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因此,大可像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。

function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}

function sayName(){
alert(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

在这个例子中,我们把 sayName() 函数的定义转移到了构造函数外部。而在构造函数内部,我们将 sayName 属性设置成等于全局的 sayName 函数。这样一来,由于 sayName 包含的是一个指向函数的指针,因此 person1 和 person2 对象就共享了在全局作用域中定义的同一个 sayName() 函数。

1.3、使用对象字面量表示法

对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。

如果留空其花括号,则可以定义只包含默认属性和方法的对象 var person = {}; //与 new Object()相同

缺点:一次性对象,无法修改属性的值。

var obj = {
name:"Daotin", // 注意是属性赋值是冒号
age: 18,
eat: function () {
console.log("我很能吃");
} // 最后一个后面没有逗号
};

obj.eat();

1.4、使用create方式创建对象

var obj = Object.create({a:1}); //根据对象{a:1}来创建对象obj

使用create创建就是通过其他的对象做为该对象的原型加入在对象中。

我们在获取 obj 的属性的时候,如果 obj 对象没有对象属性,但是有对象的原型属性(创建的时候遗传下来的属性),那么获取的就是原型属性;如果 obj 对象有自己的对象属性,那么就获取自己的对象属性。

但是在设置 obj 属性值时,只能设置自己的对象属性,不能修改遗传下来的原型属性。

示例:

var obj1 = {
a: 1
};
var obj2 = Object.create(obj1);
console.log(obj2.a); // 1
obj2.a = 10;
console.log(obj2.a); // 10
console.log(obj2); //{a: 10}a: 10 __proto__: a: 1 __proto__: Object

可以看出 obj2.a = 10; 修改的是自己的属性,而遗传的 obj1.a 还是1.

2、访问对象属性

点表示法 和 方括号表示法

alert(person["name"]); //"Nicholas"
alert(person.name); //"Nicholas"

从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要优点是可以通过变量来访问属性(属性绑定),例如:

var propertyName = "name";
alert(person[propertyName]); //"Nicholas"

如果属性名中包含会导致语法错误的字符,或者属性名使用的是关键字或保留字,也可以使用方括号表示法。

例如:person["first name"] = "Nicholas"; 由于"first name"中包含一个空格,所以不能使用点表示法来访问它。然而,属性名中是可以包含非字母非数字的,这时候就可以使用方括号表示法来访问它们。通常,除非必须使用变量来访问属性,否则我们建议使用点表示法。

PS: 如果访问一个没有的属性的值,那么这个值为 undefined,而不是报错?

因为 js 是一门动态类型的语言,不管使用点表示法还是方括号表示法,如果没有这个属性,就相当于在创建这个属性,然而这个时候没有赋值,所以就是 undefined。

3、访问对象方法

对象名.函数名();

4、对象的静态方法

对象的动态属性和方法,必须是实例化的对象才可以执行。

对象的静态方法,可以直接使用 Object.方法名 的形式来执行。

4.1、delete

删除对象的属性(包括值属性和方法属性)

var obj1 = {
a: 1,
b: function () {
alert("b");
}
};
delete obj1.b;
console.log(obj1); // {a:1}

注意:delete 不能删除window下的属性和方法。

var s=10; delete window.s; console.log(s);

不建议使用delete删除数组的元素,因为这样数组就变成松散结构(个数不变,中间有空缺)。

var array = [1, 2, 3, 4];
delete array[1];
console.log(array); // [1, empty, 3, 4]

4.2、assign

语法:Object.assign(目标对象,源对象);返回值也是目标对象。

描述:assign方法用于将所有可枚举的属性的值(包括方法)从一个或多个源对象复制到目标对象。它将返回目标对象。如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。

var obj1 = {
a: 1,
b: 2
}
var obj2 = {};
Object.assign(obj2, obj1);
console.log(obj2); // {a: 1, b: 2}

如果复制多个对象的属性时,后面的对象属性如果和前面相同就会覆盖前面的.

原型属性不能复制,不可遍历属性,不可写属性,冻结属性都不能复制

var obj1 = {
a: 1,
b: 2
}
var obj2 = {
c: 3,
b: 4
}
var obj = {};
Object.assign(obj, obj1, obj2);
console.log(obj); // {a: 1, b: 4, c: 3}

注意:assign 是浅拷贝,拷贝的仍然是引用关系。

    var obj1 = {
a: 1,
b: 2

}
var obj2 = {
c: 3,
b: {
d: 10
}
}
var obj = {};
Object.assign(obj, obj1, obj2);
console.log(obj); // {a: 1, b: {d : 20}, c: 3}
obj2.b.d = 20;
console.log(obj); // {a: 1, b: {d : 20}, c: 3}

-那么如何做到深拷贝呢?

方式一:使用JSON.parse和JSON.stringify来转换

    var obj2 = {
c: 3,
b: {
d: 10
}
}
var obj = {};
obj = JSON.parse(JSON.stringify(obj2));
console.log(obj);
obj2.b.d = 20;
console.log(obj); //{c: 3, b: {d:10}}
console.log(obj2); //{c: 3, b: {d:20}}

这种方法只适用于纯数据json对象的深度克隆,因为有些时候,这种方法也有缺陷,因为其会自动忽略值为 function 和 undefined 的属性。

方式二:使用 for in 递归拷贝

function deepCopy(obj) {
var result = Array.isArray(obj) ? [] : {}; // obj 是数组还是对象
for (var key in obj) {
// obj.hasOwnProperty(prop) 方法会返回一个布尔值,指示对象obj自身属性中是否具有prop属性。
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object') {
result[key] = deepCopy(obj[key]); //递归复制
} else {
result[key] = obj[key];
}
}
}
return result;
}

var obj2 = {
c: 3,
b: {
d: 10,
e: {
f: 20,
g: {
h: 30
}
}
}
}

var obj = deepCopy(obj2);
console.log(obj); // h:30
obj2.b.e.g.h = 100;
console.log(obj);// h:30

-assign在DOM中的使用

我们之前在设置新创建的元素的时候,总是每个属性单独写一行,这样如果有几百条属性的话,就很麻烦,而且由于比较零散,不便于管理。

而现在我们就可以使用对象的assign来设置操作DOM元素的属性。

var div1 = document.createElement("div");
var div2 = document.createElement("div");
var style = {
width: "100px",
height: "100px",
backgroundColor: "pink"
}
style.border = "1px solid blue";
Object.assign(div1.style, style);
style.border = "2px solid red";
Object.assign(div2.style, style);
document.body.appendChild(div1);
document.body.appendChild(div2);

上面的这种方式就可以很方便的把众多的属性,一次性的全部加到新创建的元素中,特别好使。

4.3、defineProperty

描述: Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

语法:Object.defineProperty(obj, prop, descriptor)

参数:

obj: 要在其上定义属性的对象。 prop: 要定义或修改的属性的名称。 descriptor: 属性描述符,是一个对象,这个对象里面有四个属性和两个方法来修饰 obj 新定义的属性。

{
value: 1, // 属性对应的值
enumerable: true, // 是否可遍历,默认 false
configurable: true, // 是否可删除,默认 false
writable: true, // 是否可修改

set: function(value) {
this._data = value;
},
get: function() {
return this._data;
}
}

示例1:

var obj = {
a: 1,
b: 2
};
Object.defineProperty(obj, "c", {
value: 3,
enumerable: true, // 是否可遍历,默认 false
configurable: true, // 是否可删除,默认 false
writable: true // 是否可修改
});
for (var i in obj) {
console.log(i); // a,b,c
}

如果 enumerable: false 的话,不会显示 c。

如果 configurable: false 的话,delete 无法删除 obj 的 c 属性。

如果 writable: false 的话,obj 的 c 属性无法修改其值。

示例2:

var obj = {
_data: 3,
a: 1,
b: 2
};

Object.defineProperty(obj, "c", {
// value: 3,
enumerable: true, // 是否可遍历,默认 false
configurable: true, // 是否可删除,默认 false
// writable: true, // 是否可修改

set: function (value) {
this._data = value;
},
get: function () {
return this._data;
}

});
obj.c = 10;
console.log(obj.c);

当我们执行 obj.c = 10; 的时候相当于使用set函数将 10 给了临时变量(一般临时变量用_开头),然后我们在获取的时候,是通过get函数来获取的。

但是这个时候我们不能写value和writable属性。会报错:Invalid property descriptor. Cannot both specify accessors and a value or writable attribute.

SOLUTION:

the error means It doesn't make sense to say if a property is writable when you explicitly state what happens when you try to write it.

So ,we should remove writable: true, we also should remove value:3 , because the value is calculated dynamically when you read it.

So ,your value should be replaced by _data.

4.4、defineProperties

语法:Object.defineProperties(obj, props)

描述:defineProperties 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。

props 是一个对象,这个对象包含了你需要定义或修改的属性名和属性值。

属性名:必须为字符串。

属性描述符:又是一个对象,就是上面的descriptor对象:

示例:

var obj = {};
Object.defineProperties(obj, {
'name': {
value: "daotin",
writable: true,
enumerable: true,
configurable: false
},
'age': {
value: 18,
writable: false,
enumerable: true,
configurable: false
}

});
console.log(obj); // {"name":"daotin", "age":18}
obj.age = 20;
console.log(obj); // {"name":"daotin", "age":18} writable为false,无法修改。

4.5、getOwnPropertyNames

描述:getOwnPropertyNames方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组集合。

语法:Object.getOwnPropertyNames(obj)

示例:

var obj = {
a: 1,
b: 2,
c: function () {

},
d: 3
}
var list = Object.getOwnPropertyNames(obj);

list.map(function (item) {
console.log(item); // a,b,c,d
});

4.6、getOwnPropertyDescriptor

描述:Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

语法:Object.getOwnPropertyDescriptor(obj, prop)

示例:

var obj = {
a: 1,
b: 2,
c: function () {},
d: 3
}

Object.defineProperty(obj, "e", {
value: "daotin",
writable: true,
enumerable: true,
configurable: false
});
var desc = Object.getOwnPropertyDescriptor(obj, "e");

console.log(desc); //{value: "daotin", writable: true, enumerable: true, configurable: false}

4.7、getOwnPropertyDescriptors

描述:getOwnPropertyDescriptors 获取一个对象的所有自身属性的描述符,返回值为所有属性对象的对象集合。

语法:Object.getOwnPropertyDescriptors(obj)

示例:

var obj = {
a: 1,
b: 2,
c: function () {

},
d: 3
}

Object.defineProperties(obj, {
"e": {
value: "daotin",
writable: true,
enumerable: true,
configurable: false
},
"f": {
value: "lvonve",
writable: false,
enumerable: true,
configurable: true
}
});
var desc = Object.getOwnPropertyDescriptors(obj);

console.log(desc.e); // {value: "daotin", writable: true, enumerable: true, configurable: false}
console.log(desc.f); // {value: "lvonve", writable: false, enumerable: true, configurable: true}

5、setter和getter

setter和getter的作用:使你可以快速获取或设置一个对象的数据。

我们之前定义一个对象的set和get是下面这样的:

var obj = {
_data: 0
};
Object.defineProperty(obj, "a", {
set: function (value) {
this._data = value;
},
get: function () {
return this._data;
}
});

现在我们可以使用字面量对象的方式添加set和get:

var obj = {
_data: 0,
a: 1,
b: 2,
set data(value) {
this._data = value;
console.log("set");
},
get data() {
console.log("get");
return this._data;
}
};
obj.data = 10; // set
console.log(obj.data); // get 10

obj.num = 20;
console.log(obj.num); // 20

可以看到set和get后面的名称(如data)和我们在设置和获取的时候的名称(如data)一致的时候才会调用。

也就是,你要设置获取的属性名 == set和get后面的属性名

并且如果你不写set,那么你就设置这个对象的属性为不可修改;

并且如果你不写get,那么你就设置这个对象的属性为不可读取;

删除getter或setter的唯一方法是:delete object[name]。delete可以删除一些常见的属性,getters和setters。

delete obj["data"]; // 这样就把set和get属性全部删除了

4、JSON

什么是JSON?

JSON 的全称为:JavaScript Object Notation(JavaScript对象表示形式:就是对象字面量)

JSON格式的数据:一般都是成对的,键值对。

JSON和对象字面量的区别? json 和对象(对象字面量)的区别仅仅在于,json 格式的数据中的键必须用双引号括起来;

var json = {
"name" : "zs",
"age" : 18,
"sex" : true,
"sayHi" : function() {
console.log(this.name);
}
};

##5、字面量的遍历

对象本身没有 length,所以不能用 for 循环遍历。要使用 for...in...

var json = {“aaa”: 1, “bbb”: 2, “ccc”: 3, “ddd”: 4}

for(var key in json){
//key代表aaa,bbb.....等
//json[key]代表1,2,3....等(k 不要加引号)
}

##6、值传递和引用类型传递

分析值传递和址传递最好的方法就是画图分析,最简单。

##7、内置对象

Math, Date, String, Array

学习的时候可以查阅在线文档: MDN 在线文档 菜鸟教程

##8、基本包装类型

本身是基本类型,但是在执行的过程中,如果这种类型的变量调用了属性或者方法,那么这种类型就不是基本类型了,而是基本包装类型。这个变量也不是普通变量了,而是基本包装类型的变量。

var str = "hello";
str = str.replace("he", "wo");
console.log(str); // wollo

需要注意的地方: 如果一个对象&&true,那么结果是true 如果一个true&&对象,那么结果是对象。

var flag = new Boolean(false);
var result = flag && true;
var result1 = true && flag;

console.log(result); // true
console.log(result1); // Boolean
var num = 10; // 基本类型
var num2 = Number("10"); // 不是基本包装类型,而是转换,这里换成 Boolean 也成立
var num3 = new Number("10"); // 这才是基本包装类型