对象
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
##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"); // 这才是基本包装类型