标签:
今天想说说JavaScript对象的知识点,涉及对象,属性,复杂数据类型,基本数据类型,作用域,继承,this关键字,和window对象等概念。
一,JavaScript对象:
1.1创建对象
在JavaScript世界,我们听的最多的一句话就是:JavaScript里一切皆对象。
的确,JavaScript里的几乎所有东西都是对象或者用起来像对象,就连“my name is sunny”这句普通句子在JavaScript里面表示也是一个对象。
在我们生活的环境,几乎所有的事物都可以看做是对象。
比如,一只猫,猫的毛是白色的,它可以发出“喵喵”的叫声。
那么我们可以这样看,猫毛是猫的属性(特征),叫声就是猫的方法(动作)。
那么在JavaScript里是如何表示对象的呢?
在JavaScript中,对象是由一组用命名值集合而成的容器。
以上面的猫为例
cat
上面的“cat”就是由一组属性名和所对应值得集合,这些属性和值构成了“cat”,一只白色毛的会说“喵喵”的猫。
然而在JavaScript中是不会用表格去表达的,它是用对象来表达的。
那么上面表格内容转换为实际的JavaScript对象是这样的:
var cat=new Object();
cat.hair="白色“;
cat.speak=function(){"喵喵"}
在JavaScript中的表示可以看出,对象只是属性的容器,每个属性都有一个名称和一个值。
在这里cat只是一个变量,只是通过创建对象(Object)将这个变量表示为了JavaScript对象,然后将一些属性赋值给这个新创建的对象,从而创建出我们的cat对象。
采用Object创建的cat对象是一种Object对象,通过调用Object构造函数而得到的空对象来创建,就是以Object构造函数作为模板来生成预定义对象。
以上的都是指明一个对象,只是表达的方式不一样,一个用表格,一个用“语言”。
我们采用Object对象作为模板创建出一个空对象,其实我们也可以自定义自己的模板对象来创建对象。
<script>
var Person=function(age,hair){//提供生成对象模板
this.age=age;
this.getAge=function(){return this.age}
}
var sunny=new Person(22)//对象实例
alert(sunny.getAge())//弹出22
</script>
我们自己定义的Person构造函数可以生成对象sunny,就像原生的array构造函数可以生成数组对象一样,只是一个是自己定义的,一个是JavaScript自带的。
JavaScript实际上是一种预包装若干原生对象构造函数的语言,这些构造函数用来生成一些表达特定类型值
(number,string,function,object,array等)的复杂对象。
1.2构造函数:
构造函数的作用是创建多个共享特定特征和行为的对象,这个对象会提供一些默认的属性和默认的方法。
就是我们上面所说的提供一个模板,然后你们就用这个模板的基础上做出修改或者添加新东西。
总的来说,构造函数在你没有用new来调用时,它也仅仅只是一个普通的函数(function),
但如果你用new来调用时,JavaScript就会赋予这个函数一些不一样的权利,如,此函数内的this值就设置为正在创建的新对象,另外,此函数还会默认返回这个新创建的对象,该返回对象就是构建该对象构造函数的实例。
构造函数返回的对象被称为实例
当一个函数与new关键字一起使用时,它就会创建一个拥有构造函数内部定义的属性和值的对象。
在使用new关键字时,this指向的就是构造函数所创建的对象实例,但如果创建的构造函数没有用new关键字进行调用,那么this指向的就是该函数的“父”对象(方法)。
1.3JavaScript原生的对象构造函数
JavaScript提供了9个原生(内置)对象构造函数,
它们分别是“number,string,Boolean,function,object,array,date,regexp,error”。
JavaScript就是使用这些对象来构造JavaScript语言的。
就是说这些构造函数是不需要我们定义的,直接使用即可。
大家也许会觉得奇怪,怎么没有“Math“?
Math对象和其他的对象是有很大区别,它是一个静态对象,而不是构造函数。
这意味着它是不需要被new出来使用的,它可以直接被使用,如Math.PI。
这里值得注意的是:
Number(),String()和Boolean()构造函数不仅能构建对象也能为字符串,数字和布尔值提供基本数据值,这取决于你是如何使用它们的。
如果直接调用这些构造函数,那么就会返回一个复杂数据值(对象),
如果只是简单的在代码中表示一个数字,字符串或者布尔值(“my name is sunny”,5,true),那么它就会返回一个基本数据值。
1.4:自定义对象构造函数
在里面的例子中,我们可以看到,我们是可以自己创建自己需要的构造函数,从中可以生成多个自定义对象。
<script>
var Person=function(age,hair){
this.age=age;
this.getAge=function(){return this.age}
}
var sunny=new Person(22)
alert(sunny.getAge())//
var Kris=new Person(23)
alert(Kris.getAge())//
</script>
通过传递特定的参数和调用Person()构造函数,我们可以创建大量特定的people对象。
当你需要多个举个相同属性但具有不同值得对象时,这是很便捷的。
1.5:字面量值
JavaScript提供了一些创建“字面量”的快捷方式来创建原生对象值,而不必使用new XX()这种方式。
字面量语法:var name="sunny".
需要注意的是,
字符串,数字和布尔值当做字面量使用时,它们的值具有的是基本类型值的特点而不是复杂类型值(对象)的特点。
但是当它们被视为对象的情况下就会拥有复杂对象的特点。
就是说,在尝试使用与构造函数关联的方法或检索属性之前,就一直作为基本数据类型来使用,直到这种情况发生,JavaScript就会在幕后为此字面量创建一个临时的包装器对象,以便将该值视为一个对象。一旦执行完后,JavaScript就会去除掉这个临时的包装器对象,该值就又作为基本数据类型值来使用。这就是为什么尽管我们只是普通的定义一个字符串却还可以调用字符串对象方法的原因。
字面量创建值返回的是基本数据类型值,new创建值返回的是复杂数据类型值(对象)。
string,number和Boolean构造函数是有两种目的创建值的,分别是字面量以及复杂值。
1.6:基本数据类型
10,“sunny”,true,null和undefined等JavaScript值都被视为基本数据类型值,其中null和undefined是不需要构造函数也没有使用new来将自己创建为复杂数据类型值(对象)。
1.7,存储和复制基本数据类型值
我们先看一个例子:
<script>
var str01="sunny";
var str02=str01;
alert(str02);//sunny
str01="Kris";
alert(str02)//sunny
</script>
大家看到没有?
我们把str01的值复制给str02,然后修改str01的值,str02没有受到任何的影响。
就是说基本数据类型值是按值来存储的,而等下我们说到的复杂数据类型值却是按引用来存储值的。
1.7存储和复制复杂数据类型值
我们再看一个例子
<script>
var obj={};
var obj1=obj;
obj.name="sunny";
alert(obj.name+"===="+obj1.name);//都是sunny
obj.name="Kris";
alert(obj.name+"===="+obj1.name);//都是Kris
</script>
当一个改变了另一个也跟着改变,因为他们引用的是同一个引用地址。
注意:
使用new关键字创建String(),Number()和Boolean()值时,或者这些值在幕后被转换成复杂对象时,它们的值依然是按照值来进行存储的。
<script>
var str1=new String("sunny");
var str2=str1;
alert(str1+"======="+str2);//sunny,sunny
str1=new String("Kris")
alert(str1+"======="+str2);//Kris,sunny
</script>
还有一个地方需要注意的是,
指向内存中复杂对象的变量,只有在引用相同地址时才是相等的,相反,两个单独创建的对象,即使具有相同的类型和完全相同的属性,它们也是不相等的。而我们上面的所说的基本数据类型,只要它们的值相同就相等。
<script>
var obj1={name:"sunny"};
var obj2={name:"sunny"};
alert(obj1==obj2)//false
</script>
复杂值是通过引用进行存储和操作的。
创建一个包含复杂对象的变量时,其值是内存中的一个引用地址。
1.8:复杂对象具有动态属性
复杂对象支持动态对象属性,因为我们可以定义对象,然后创建引用,再更新对象,并且所有指向该对象的变量都会获得更新。
JavaScript中的对象是动态的,这使得JavaScript中的对象是可以改变的。也就是说我们可以在原生构造函数上哪存储属性,并在原型对象上,向原生对象额外添加新方法。本质上说,整个JavaScript语言都可以变为自定义版本。如果更改了JavaScript的原生内部运作机制,你可以获得一个自定义版本的JavaScript来进行程序处理。
1.9,指向其构造函数的constructor属性
构造函数实例都拥有指向其构造函数的constructor属性。
任何对象实例化时,都是在幕后将constructor属性创建为对象/实例的属性,这属性是指向创建该对象的构造函数的。
<script>
var str=new String("sunny");
alert(str.constructor===String)//true
alert(str.constructor)//输出string的构造函数代码
</script>
在字面量/基本数据类型值上使用constructor属性都能正确的指向构造函数。
<script>
var str="sunny";
alert(str.constructor===String)
alert(str.constructor)
</script>
constructor属性也适用于用户自定义的构造函数
1.20:验证对象是否是某构造函数实例的instanceof操作符
通过使用instanceof操作符,可以确定(true or false)一个对象是否是某个构造函数的实例,它也是适用于自定义对象。
<script>
var str1=new String("sunny");
alert(str1 instanceof String)//true
var str2="sunny";
alert(str2 instanceof String)//false
</script>
注意的是,基本数据类型就算是当做对象使用(对象包装器)判断也为false,只有用new创建时才为true。
所以,instanceof只适用于构造函数返回的复杂对象和实例。
1.21:返回正在使用值的类型的taype操作符
<script>
var str="sunny";
alert(typeof str)//string
</script>
1.22:构造函数创建的实例可以拥有自己独立的属性和方法
在JavaScript中,对象在任何时候都可以扩展(即动态属性)
<script>
var str1=new String("sunny");//创建一个实例
str1.age=22;//扩展该实例属性
alert(str1.age)//22
var str2="sunny";
str2.age=22;
alert(str2.age)//undefined
</script>
从上面的例子可以看出,字面量不能实现此功能。
除了自己的属性外,实例还可以拥有继承自原型链的属性
二:对象与属性
2.1:用点表示法或中括号表示法获取/设置/修改对象属性
<script>
var Person=function(name){
this.name=name;
}
var per=new Person("sunny")
alert(per.name);//sunny
alert(per["name"]);//sunny
var str_name="name";
alert(per[str_name]);//sunny
</script>
我们比较常用的是点表示法,但是需要用变量来获取的话,那就只能用中括号表示法。
因为对象可以包含其他对象,所以,obj.obj1.obj2.name来获取属性是很常见的,这就是我们说的“对象链”。
2.2删除对象属性的delete操作符
delete操作符可以用于将属性从一个对象中完全删除。但是delete不会删除在原型链上找到的属性。delete是将一个对象中属性删除的唯一方法,将属性定义为undefined或者null也只是改变了它的值,并没有把属性从对象中删除。
<script>
var Person=function(name){
this.name=name;
}
var per=new Person("sunny")
alert(per.name);//sunny
delete per.name
alert(per.name);//undefined
</script>
2.3:属性的引用
如果试图访问对象中没有的属性,JavaScript会一直试图在原型链上来找,如果一直找到原型链的末端都没有,那么就会返回undefined。
当试图访问一个对象的属性时,JavaScript会检查该属性的对象实例,如果该实例拥有属性,那么就直接返回该实例中属性值,但是这时候是没有继承发生的,因为利用到原型链。
JavaScript中所有的对象实例独有一个属性,我们称为_proto_的链接,它链接到实例的构造函数,我们可以利用这个链接来获取构造函数,特别是实例构造函数的prototype属性。
我们前面说过,JavaScript中所有皆对象,函数也是一个拥有属性都属性,那么说是对象从其他对象中获得(继承)属性也是可以的。JavaScript通过prototype对象默认的为原生对象完成这些工作,换成我们自己定义的构造函数中,我们也是可以利用原型链的。
<script>
var arry1=["sunny","Kris"];
alert(arry1.length)//2
arry1.push("summer")
alert(arry1.length)//3
</script>
奇怪,我们在创建数组实例的时候并没有为它定义push()方法,那为什么arry1实例却可以使用这个方法呢?
答案就是,push方式是被定义在Array.prototype中的,这样使得所有的数组实例都可以直接使用这个方法,而不需要各自定义,当然如果你是想重写此方法 ,我就无话说。
如果你试图访问一个属性,但该对象不包含该属性时,JavaScript将针对这个属性搜索原型链。
在上面的例子中,JavaScript将查看创建对象(Array)的构造函数,并检验其原型(Array.prototype),看是否可以找到这个属性,如果没有找到,JavaScript将继续搜索初始构造函数背后的构造函数链,一直搜索到链的末端。
由于所有的prototype属性都是对象,所以链中的最后一个连接是Object.prototype。
2.4:使用hasOwnProperty()检查来自非原型链属性的对象
in操作符可以检查一个对象是否含有一个属性,包括来自原型链上的属性。
hasOwnProperty方法可以检查来自非原型链属性的对象。
当需要确定一个属性是对象本地属性还是继承自原型链的属性时,可以使用hasOwnProperty方法。
<script>
var per={name:"sunny"}
alert("name" in per)
</script>
<script>
var per={name:"sunny"}
alert(per.hasOwnProperty("name"))//true
</script>
判断per是否拥有它自己的名为name的一个属性。
2.5:使用for in枚举对象的属性
<script>
var per={name:"sunny",age:1}
for(var i in per){
if(per.hasOwnProperty(i)){//避免来自原型链
alert("key:"+i+"val:"+per[i])
}
}
</script>
for in 不仅会遍历特定对象的本地属性(自身属性),还会遍历所继承(通过原型链)的对象上的属性。访问属性的顺序也不总是它们出现在循环内部被定义时的顺序,另外定义属性的顺序也不一定是访问它们的顺序。
fo
三:JavaScript的常用对象
3.1:Object()对象
我们可以使用内置的Object()构造函数创建动态的普通空对象,这个对象是没有预定义属性和方法的。
Object()拥有prototype属性。
原型链是以Object.prototype结尾的,所以Object()的所有属性和方法都被所有JavaScript对象继承。
Object.prototype是JavaScript寻找值的最后一个位置。
3.2Function()对象
函数是代码语句的容器,可以使用圆括号操作符()来调用.调用函数时,参数可以在圆括号内传递,以便函数中的语句可以访问这些特定值。
函数可用于返回值,构建对象,或者单纯的作为简单的代码运行。
Function()拥有prototype属性。
JavaScript函数是对象,
这意味着函数可以存储在一个变量,数组或者对象中。
函数总是有返回值,如果没有指定返回值,则会返回undefined。
3.2.1函数的参数
在调用函数时,参数是将值传递给函数作用域的工具,在JavaScript中省略参数是合法的,即使定义了接收这些参数,省略的参数会被赋予了undefined值。
如果传递的参数数量多于定义的参数数量,也是合法的,这时候可以从arguments对象中访问这些参数。
3.2.2:this和arguments适用于所以函数
arguments对象是一种类数组对象,它包含所有传递给函数的参数,即使在定义函数时不指定参数,在调用时还是传入参数,我们就可以利用arguments数组来访问。
<script>
var fun=function(){
alert(arguments[0])
};
fun("hi")//hi
</script>
传递给所有函数的this关键字都是对包含函数的对象的引用,就是说,作为属性包含在对象内的函数(即方法),可以使用this来获得对“父”对象的引用,当函数在全局作用域中定义时,this值就是全局对象。
<script>
var per1={
name:"sunny",
fun:function(){return this}//object 这里指向的是per1这个对象
}
alert(per1.fun())
var fun2=function(){return this}
alert(fun2())//window
</script>
对象属性是可以带双引号的。
3.2.3arguments对象中的callee属性
arguments中有一个名为callee属性,它是对当前执行函数引用。该属性可以用于从函数的作用域内引用函数本身--自用引用。
<script>
var fun=function fun(num){
alert(num)
arguments.callee(333)
}
fun(222)
</script>
3.2.4:代码没有执行完成前取消函数执行的return关键字
可以通过返回有值或无值的return关键字在条用时随时取消函数的执行,也就是通过return可以在函数的任意点取消函数的执行。
<script>
var fun=function(){
return
alert(2222)
}
fun()
</script>
3.2.5:函数的定义(声明)
函数的定义有三种不同的方式:函数构造函数,函数声明和函数表达式。
函数构造函数(不推荐)数据类型转换会很麻烦。
<script>
var fun1=new Function("num1","num2","return num1+num2")//最后哦一个参数是函数体
alert(fun1("1","2"))
</script>
函数声明
<script>
function fun(num1,num2){
alert(num1+num2)
}
fun(1,2)
</script>
函数表达式
<script>
var fun=function(num1,num2){
alert(num1+num2)
}
fun(1,2)
</script>
注意的是:
使用不带圆括号的函数名是访问函数指针,而非调用函数。
3.2.6函数声明与函数表达式
用函数声明方式或函数表达式方式定义的函数在被解析时所执行的方式并不是一样的。
在被解析时,解析器会优先读取函数声明,并使其在任何代码之前可用,也就是说定义的函数的语句可以在调用函数语句的后面。
至于函数表达式,则必须等到解析器执行到它所在的代码行时,才会真正被解析执行,也就是说调用它的语句必须在定义它的语句后面。
我们都知道,解析器,这里就说浏览器吧,在解析JavaScript代码时候,都是一行一行从上往下顺序执行的,但如果你是用函数声明方式定义出的函数,那么浏览器在加载时候就会优先解析它们,而用函数表达式的函数就只能按正常的流程处理了。
3.2.7函数的调用
作为函数:fun();
作为方法:obj.fun();
作为构造函数:new fun()
使用apply()或call()方法:apply(fun,"参数1“,“参数2”),call(fun,["参数1",“参数2”])
call()与apply()之间的区别是函数调用时,参数传递的不同,前者传递多个以逗号分开的参数,后者传递多个参数组成的数组。
3.2.8,匿名函数
匿名函数值得是没有给出名字的函数,一般用在将函数作为参数传递给另一个函数。
<script>
var fun=function(fun2){
fun2()
}
fun(function(){alert(2)})
</script>
3.2.9,自调用的函数表达式
通过圆括号操作符,可以在定义函数表达式后立即调用函数(除用function()构造函数创建的函数)
<script>
var fun=function(num){
alert(num)
}(2)
</script>
3.2.10自调用的匿名函数语句
<script>
(function(num){
alert(num)
})(2)
(function(num){
alert(num)
}(3))
</script>
3.2.11函数可以调用自身(递归)
<script>
var fun=function fun(num){
alert(num)
fun(333)
}
fun(222)
</script>
四:this关键字
创建函数时,系统会创建一个名为this的关键字,它链接到运行该函数的对象。
this对其函数的作用域是可以见的。
this是在函数内部使用,用来引用包含函数的对象,而不是函数本身,使用new或call()和apply()的情况除外。
this值是基于在运行时调用函数的上下文。
4.1:在嵌套函数中用this引用的是window对象
因为在嵌套函数中,this失去了方向,所以引用的就是window对象而不是定义函数所在的对象。
<script>
var obj={
fun1:function(){
alert(this)//object
var fun2=function(){
alert(this)//window
}()
}
}
obj.fun1()
</script>
<script>
var fun=function(fun2){
fun2()
}
fun(function(){alert(this)})//windown匿名函数内的this指向的window
</script>
<script>
var obj={
fun1:function(fun){
alert(this)//object
fun()//window
var fun2=function(){
alert(this)//window
}()
}
}
obj.fun1(function(){alert(this)})
</script>
当this值的宿主函数被封装在另一个函数的内部或在另一个函数的上下文被调用时,this值都是对window的引用。
可是我们想在嵌套函数中this指向父对象,该怎么做呢?
可以在父函数中使用作用域链来保留对this的引用,以便this值不丢失。
<script>
var obj={
fun1:function(fun){
alert(this)//object
var that=this//在父函数中保留this到that变量中
fun()//window
var fun2=function(){
alert(that)//object
}()
}
}
obj.fun1(function(){alert(this)})
</script>
4.2:使用call()或apply()控制this值
我们都知道this值通常取决于调用函数的上下文,除了后面我们将要说的new创建函数,但我们可以使用apply()或call()来重写this的值,以便在调用函数时定义this指向哪个对象。
使用这个方法就好像在说,“我在调用的是X函数,但是你要告诉X函数把Z对象作为this值使用”
这样做可以改变JavaScript中决定this值的方式。
<script>
var obj={
}
var fun=function(name){
this.name=name;
alert(this.name)
}
fun.call(obj,"Kris")//obj中的this设置为fun中的this
alert(obj.name)
</script>
4.3,自定义构造函数内部使用this关键字
在使用new关键字调用函数时,this值(在构造函数中声明)引用实例本身。
<script>
var Person=function(name){
this.name=name;
}
var per1=new Person("sunny")
</script>
当我们用Person创建出per1这个对象(实例)时,Person中的this指的就是per1这个对象。
使用new关键字调用构造函数时,this引用的是“即将创建的对象”,
如果不使用new关键字,this值将是调用函数的上下文。
4.4:原型方法内的this关键字引用的是构造函数实例
当在添加至构造函数的prototype属性的的函数中使用this,this引用的是调用方法的实例.
<script>
var Person=function(name){
this.name=name;
}
Person.prototype.getName=function(){//向person.prototype添加getname方法以便所有的person实例都能继承该方法
alert(this.name)//this引用的是调用它的实例,这里是per1
}
var per1=new Person("sunny")
per1.getName()//sunny
</script>
如果this指向的实例或者对象不包含被引用的属性,其他关于属性查找规则在这里也同样适用,将在原型链上查找属性。在我们这个例子中,如果getName()在实例中找不到name属性,那么将先在Person.prototype中查找,如果找不到就在Object.prototype中查找,如果都没有则返回undefined。
说说JavaScript那些事
标签:
原文地址:http://blog.csdn.net/qianqianyixiao1/article/details/51103255