对象 Object
javascript简单类型:数字、字符串、布尔值、null和undefined,其他的都为对象。数字、字符串、布尔值虽然拥有方法,但它们是不可变的,所以不能称为对象。
数组 Arry 是对象,函数 Function 是对象,正则表达式 RegExp 也是对象,对象自然也是对象。对象是属性的容器,其中每个属性都拥有名字和值。属性的名字可以是空字符串在内的任意字符串。属性值可以是除undefined值以外的任何值。JS里的对象是无类型的,且允许对象继承和嵌套。
1.对象字面量
对象字面量:一个对象字面量就是包围在一对花括号中的零或多个"名/值"对,它可以方便的创建新对象值。对象字面量可以出现在任何允许表达式出现的地方。如果属性名是一个合法的JS标识符且不是保留字,则并不强制要求用引号括住属性名。JS的标识符包含连接符(-)是不合法的,但允许包含下划线(_)。
var empty_Obj = {};
var user = {
"first-name" = "Jack",
"last-name" = "Howard",
"age" = "19"
}
对象是可以嵌套的
var flight = {
airline: "Eastern Airlines",
number: 2021,
departure: {
time: "2021-06-07 09:00",
city: "Shanghai"
},
arrival: {
time: "2021-06-07 11:30",
city: "Beijing"
}
};
2.检索
检索对象里包含的值,可以使用 . 表示法或[]检索对象,优先考虑使用.表示法,因为它更紧凑且可读性更好。如果字符串表达式不是合法的JS标识符,则必须使用[]来检索对象。
user["first-name"] // "Jack"
flight.arrival.city // "Beijing"
检索一个不存在的成员属性的值将返回undefined,可以使用||运算符填充默认值。
flight.status // undefined
var middle = user["middle-name"] || "(none)";
3.更新
对象里的值可以通过赋值语句来更新。如果属性值已经存在于对象里,则这个属性的值会被替换,否则该属性就会被扩充到对象中。
//属性名已经存在于对象里的,属性值被替换
user['first-name'] = 'Mike';
//属性名未存在于对象里,属性被扩充到对象中
user.nickname = 'Curly';
4.引用
对象引用:对象通过引用来传递,它们永远不会被复制
var x = user;
x.nickname = 'Curly';
var nick = user.nickname;
// 因为x和stooge 是指向同一个对象的引用,所以这里nike为'Curly'。
var a = {}, b = {}, c = {};
//a,b,c引用不同的空对象
a = b = c = {};
//a,b,c引用同一个空对象
5.原型
每个对象都会连接到一个原型对象,并且它可以从中继承属性。所有通过字面量创建的对象,都连接到 Object.prototype 这个JS中的标准的对象。
当创建一个新对象时,可以选择某个对象作为它的原型,给Object增加一个create方法,这个方法创建一个使用原对象作为其原型的新对象。
//Object.create 内部原理
if(typeof Object.beget !== 'function') {
Object.create = function(o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
var another_user = Object.create(user);
原型连接在更新时是不起作用的。当我们对某个对象作出该表时,是不会触及改对象的原型。
another_user['first-name'] = 'Harry';
another_user.nickname = 'Moe';
原型连接只有在检索值的时候才被用到。如果我们尝试去获取对象的某个属性值,但该对象没有此属性值,那么再从它的原型中寻找,以此类推,直到该过程最后到达终点Object.prototype。如果想要的属性完全不存在于原型链中,那么结果就是undefined值。这个过程称为委托。
原型关系是一种动态的关系。如果我们添加一个新的属性到原型中,该属性会立即对所有基于该原型创建的对象可见。
user.profession = 'actor';
another_user.profession // 'actor'
无论什么时候,只要创建了一个新的函数(并不一定要是构造函数),就会根据一组特性的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。比如,user.prototype.constructor 指向 user。而通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法。
创建了自定义的构造函数之后,其原型对象默认只会取得 constructor 属性;至于其他方法,则都是从 Object 继承而来的。当调用构造函数创建一个新的实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。
原型就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。 换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
6.反射
反射:检查对象并确定对象的属性。typeof操作符可以方便的确定属性的类型。
typeof flight.number //"number"
typeof flight.airline //"string"
typeof flight.arriver //"object"
typeof flight.manifest //"undefined"
注意原型链中的任何属性都会产生值:
typeof flight.toString // 'function'
typeof flight.constructor // 'function'
处理不需要的属性:
1.让你的程序做检查并丢弃掉值为函数的属性。
2.使用hasOwnProperty方法可以检查对象是否拥有独有的属性,如果有则返回true,hasOwnProperty方法它不会检查原型链。
flight.hasOwnProperty('number') //true
flight.hasOwnProperty('constructor') //false
7.枚举
for in 语句可用来遍历一个对象中的所有属性名。该枚举过程将会列出所有的属性——包括函数和原型中的属性和你不关心的原型中的属性,所以需要过滤掉那些你不想要的值。最常用的过滤器是hasOwnProperty方法,及使用typeof排除函数。
var name;
for (name in another_stooge) {
if (typeof another_stooge[name] !== 'function') {
document.writeln(name + ':' + another_stooge[name]);
}
}
for in遍历,属性名出现的顺序是不确定的。如果想要确保属性以特定的顺序出现,则创建一个数组,将属性以正确的顺序放入,使用for获取它们的值。
8.删除
delete 运算符可以用来删除对象的属性。它不会触及原型中的任何对象。删除对象的属性可能会让来自原型链中的属性透现出来。
another_user.nickname // 'Moe'
//删除 another_user 的 nickname 属性,暴露了原型的 nickname属性
delete another_user.nickname;
another_user.nickname // 'Curly'
9.减少全局变量污染
JavaScript可以很随意地定义全局变量来容纳你的应用的所有资源。遗憾的是,全局变量会削弱程序的灵活性,应该避免使用。
最小化使用全局变量的方法之一:为你的应用只创建一个唯一的全局变量。
var MYAPP = {};
//该变量变成了整个应用容器
MYAPP.user= {
"first-name": "Jack",
"last": "Howard",
"age": "19"
};
只要把全局性的资源放到一个名称的空间下。程序与其他应用程序、组件或类库之间发生冲突的可能性就会显著地降低,程序也看起来更容易阅读,因为很明显,MYAPP.user指向的是顶层结构。使用闭包来进行信息隐藏也是一种减少全局污染的方法。