学习内容摘自 阮一峰博客
目录
- 概述
1.1生成方法
1.2键名
1.3属性
1.4对象的引用
1.5表达式还是语句 - 属性的操作
2.1读取属性
2.2检查变量是否声明
2.3属性的赋值
2.4查看所有属性
2.5delete命令
2.6in运算符
2.7for..in循环 - with语句
1概述
1.1生成方法
对象是javascript核心概念,也是最重要数据类型,javascrit所有数据都可以被视为对象
简单说,所谓对象,就是一种无序集合,由键值对构成key-value
var o = {
p:'hello world',
m:function(){...},
n:'heheda'
};
p是键名 hello world是value,之间用:冒号分割,
属性(属性)与方法(属性)之间用逗号隔开
对象的三种生成方法:
- 直接使用大括号生成{}
- new Object()形式生成
- Object.create(null)方式生成;
var o1 = {};
var o2 = new Object();
var o3 = Object.create(null);一般来说第一种大括号简洁,第二宗写法比较清晰,第三种写法一般用在需要对象继承的场合.
1.2键名
对象的所有键名都是字符串,所以加不加引号都可以
var o = {
'p':'hello world'
};
//如果键名是数值,会被自动转为字符串
var o ={
1:'a',
2.2:'b',
1e2:true
};
但是呢,以上有些标识名是不符合规范的,所以要加上单引号
另外:其实单引号或者双引号,javascript都是支持的,只是说html css等都是双引号,所以javascript则一律用单引号,养成一个比较好的编程习惯而已
1.3属性
对象的每一个键名又称为属性property,如果属性的值为函数,则称为方法
var m = {
p:123,
n:function(){...}
};
属性或方法最后一个逗号可加可不加 都不会报错
属性可以动态创建,不用一开始就指定,这个比java里面的类创建条件可宽松*多了….
var o1 = {};
o1.m = 'haha';
o1.n = function(){...};
1.4对象的引用
如果不同的变量名指向同一个对象,那么他们都是这个对象的引用,也就是说指向同一个内存地址,修改其中一个变量,会影响到其他所有变量
var o1 = {};
var o2 = o1;
o1.a = 1;
console.log(o2.a);//1
o2.a = 3;
console.log(o1.a);//3
o2.m = 4;
console.log(o1.m);//4
上面代码中,o1和o2指向同一个对象,因此为其中给任何一个变量添加属性,另一变量都可以读写该属性,
此时,如果取消某一个变量对于原对象的引用,不会影响到另一个变量
var o1 = {};
var o2 = o1;
o1 = 1;
console.log(o2);//{}
上面代码中,o1和o2指向同一个对象,然后o1的值变为1,这时不会对o2产生影响,o2还是指向原来那个对象
其实这个java还是很不一样的,java中如果地址引用,那么是链路式的,只会跟着上一个对象走,但javascript不是的,它是直接获取到了对象地址,从中间人o1获取到数据之后,就不会和中间人之间有关系了,只是说目前共同引用了一个地址而已.
---
---
var x = 1;
var y = x;
x = 2;
console.log(y);//1
很显然的结果,由于原始类型的数据时传值,所以当你获得值之后,和你的上一个变量就没半毛钱关系了.
1.5表达式还是语句
对象采用大括号表示,这导致了一个问题:如果行首是一个大括号,他到底是表达式还是语句?
{foo:300}
为了避免这种奇异,javascript规定.如果首行是大括号一律解释为语句即代码块,如果要解释为表达式,必须在大括号前加上圆括号.
这种差异在eval中反应的非常明显
eval('{foo:123}');//123
eval('({foo:123})');//{foo:123}
上面代码中,如果没有圆括号,将其理解为代码块,加上圆括号,就理解成一个对象
var o = {
p:’hello world’
};
o.p;//hello world
o[‘p’];//hello world
注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当做变量处理,但是,数字键可以不加引号,因为会被当做字符串处理
var p = {
0.4:'hello world'
};
o['0.4']; //hello wrold
o[0.4];//hello world
---
---
方括号运算符内部可以使用表达式
o['hello'+'world'];
o[3+3];
//数字键名不能使用点运算符(因为会被当小数点),只能使用方括号运算符
2.2检查变量是声明
如果读取一个不存在的键,会返回undefined,而不是报错,可以利用这一点,来检查一个全局变量是否被声明.
//检查a变量是否被声明
if(a){...}//报错
if(window.a){...}//不报错
//node中if(globa.a){...}
if(window['a']){...}//不报错
上面的后两种写法之所以不报错,是因为浏览器环境,所有全局变量都是window对象的舒心g,window.a的含义就是读取window对象的a属性,如果该属性不存在,就返回undefined,并不会报错.
但是,后两种有个缺点,就是a可能值为false,0或者null
那么这样判断是不合理的,更好的方式是
if('a' in window){
//变量声明过
}else{
//变量未声明过
}
2.3属性的赋值
点和方括号运算符不仅可以读取对象的值,还可赋值
javascript允许属性的后绑定,也就是说,你可以在任意时刻新增属性,没必要在定义对象的时候,就定义好属性
示例:
o.p = 'abc';
o['p'] = 'abc;
---
---
var o = {};
o.p = 1
2.4查看所有属性
查看一个对象本身所有的属性,可以使用Object.keys方法
var o = {
key1:1,
key2:2
};
Object.keys(o);
//['key1','key2'];
2.5delete命令
delete命令用于删除对象的属性,删除成功后返回true.
var o = {p:1};
Object.keys(o);//["p"]
delete o.p //true
o.p//undefined
Object.keys(o)//[]
上面代码,delete命令删除了o对象的p属性,删除后,再读取p属性就会返回undefined,而且Object.keys方法的返回值中,o对象也不再包括该属性.
注意,delete即使所删除的对象属性,不存在,delete也不会报错而且返回true
var o = {};
delete o.p;
因此不能根据delete来确定某属性是存在的,只能确定该属性为undefined.
只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除
var o = Object.defineProperty({},'p',{
value:23,
configurable:false
});
o.p//123
delete o.p//false
delete只能删除对象本身的属性,无法删除继承的属性.
var o = {};
delete o.toString //true
o.toString //function toString(){[native code]}
上面代码中,toString是对象o继承的属性,虽然delete命令返回true,该属性并没有被删除,毅然存在.
最后,delete命令不能删除var 命令声明的变量,只能用来删除对象属性.
var p = 1;
delete p//false
delete window.p//false
上面命令中,p是var命令声明的变量,delete命令无法删除它,返回false.因为var声明的全局变量都是顶层对象的属性而且默认不得删除
2.6in运算符
in运算符用于检查对象是否包含某个属性,如果包含就返回true否则返回false
var o = {p:1};
'p' in o;//true
在javascript语言中,所有全局变量都是顶层对象(浏览器的顶层对象就是window对象)的属性,因此可以用in运算符判断,一个全局变量是否存在.
//假设变量x未定义
//写法一:报错
if(x){return 1;}
//写法二:不正确
if(window.x){return 1;}
//如果x设置为null或者0或者false的时候检查不出来
//写法三:正确
if('x' in window){return 1;}
in运算符的一个问题是,它不能识别对象继承的属性
var o = new Object();
o.hasOwnProperty('toString');//false
'toString' in o;//true
上面代码中,toString方法不是对象o,自身的属性,而是继承的属性,hasOwnProperty方法可以说明这一点.但是,in运算符不能识别,对继承的属性也返回true.
for…in循环
var o = {a:1,b:2,c:3};
for(var i in o){
console.log(o[i]);
}
下面是一个使用for…in循环,提取对象属性的例子
var obj = {
x:1,
y:2
};
var props = [];
var i = 0;
for(props[i++] in obj);
console.log(props);
for…in循环有两个使用注意点.
它遍历的是对象所有可便利(enumerable)的属性,会跳过不可遍历的属性
它不仅遍历对象自身的属性,还遍历继承的属性.
请看下面的例子.
//name是person本身的属性
function Person(name){
this.name = name;
}
//describe是Person.prototype的属性
Person.prototype.describe = function(){
return 'Name: '+this.name;
};
var person = new Person('jane');
//for...in循环会遍历实例自身的属性(name)
//以及继承的属性(describe)
for(var key in person){
console.log(key);
}
//name
//describe
如果只遍历对象本身的属性,可以再加一个判断
for(var key in person){
if(person.hasOwnProperty(key)){
console.log(key);
}
}
//name
person.toString()//[object object]
这个toString属性不会被for…in循环遍历到,因为他默认设置为不可遍历
一般情况下,都只是想遍历对象自身的属性,所以不推荐使用for…in循环
3with语句
with语句的格式如下
with(object){
statements;
}
//他的作用是操作统一个对象的多个属性时,提供一些书写方便
with(o){
p1 = 1;
p2 = 2;
}
//等同于
o.p1 = 1;
o.p2 = 2;
//例2
with(document.link[0]){
console.log(href);
console.log(title);
console.log(style);
}
//等同于
console.log(document.links[0].href);
console.log(document.links[0].title);
console.log(document.links[0].style);
注意,with区块内部的变量,必须是房钱对象已经存在的属性,否则会创造一个当前作用于的全局变量,这是因为with区块没有改变作用域,它的内部毅然是当前作用域,素以不要使用with…