web前端 八
对象(object)
js中数据类型:
- String字符串
- Number数值
- Boolean布尔值
- Null空值
- undefined未定义
——以上五种数据类型属于基本数据类型。当看到的值不是以上五种时,则全是对象 - Object对象
基本数据类型的不足:
基本数据类型都是单一的值。如"hello",123,true,值和值之间无任何联系
对象:
对象属于一种复合的数据类型,在对象中可保存多个不同数据类型的属性
对象的分类:
- 内建对象
由ES标准中定义的对象,在任何的ES的实现中都可以使用。
如:Math,String,Number,boolean,Function,Object… - 宿主对象
由js运行环境提供,目前来讲主要是指由浏览器提供的对象
如:BOM,DOM - 自定义对象
由开发人员自己创建的对象
对象的基本操作
创建对象
var obj= new Object();
使用new关键字调用的函数是构造函数,构造函数是专门用来创建对象的函数。
使用typeof检查一个对象时会返回object
向对相添加属性
在对象中保存的值称为属性
语法:对象.属性名=属性值;
如:向obj中添加name,age,gender属性
<script>
var obj= new Object();
obj.name="小灰灰";
obj.gender="男";
obj.age=7;
console.log(obj);
</script>
读取对象中的属性
语法:对象.属性名
console.log(obj.age);
如果读取对象中没有的属性,不会报错而是会返回undefined
修改对象的属性值
语法:对象.属性名=新值
删除对象的属性
语法:delete 对象.属性名;
使用工厂方法创建对象
可批量创建对象,用函数储存,需要时调用。
方法如下:
function createPerson(a,b,c){
var obj=new Object();
obj.name=a,
obj.age=b,
obj.gender=c
return obj;
}
var obj2= createPerson("单位",12,"面积");
console.log(obj2);
属性名和属性值
属性名:
对象的属性名不强制要求遵守标志符的规范。什么都可,但尽量遵守标准符规范
注:如果要使用特殊的属性名不能采用(.)的方式来操作,而要另一种方式
语法:对象[“属性名”]=属性值
obj["123"]="灰太狼";
console.log(obj["123"])
使用[]这种形式去操作属性,更加灵活。在[]中可以直接传递一个变量,这样变量值是多少就会读取那个属性
如
obj["123"]="灰太狼";
var a="123"
console.log(obj[a])
属性值:
js对象的属性值,可以是任意的数据类型甚至也可以是一个对象。
in运算符:
可检验一个对象中是否含有指定的属性,如果有返回·true没有则返回false
语法:“属性名” in 对象
如:检查obj中是否有test2属性
var obj= new Object();
obj.name="小灰灰";
obj.gender="男";
obj.age=7;
console.log("text2"in obj);
基本数据类型和引用数据类型
基本数据类型: undefined Null Boolean Number String
引用数据:object
- JS中的变量都是保存在栈内存中的,基本数据类型的值都是直接储存在栈内存中的,值与值之间独立存在,修改一个变量不会影响另一个变量。
- 对象保存在堆内存中间,每创建一个新对象,就会在堆内存中开辟一个新空间。而变量保存的是对象的内存地址(对象的引用)。当两个变量保存的是同一个对象地址时,在一个变量中修改了对象的属性,另一个变量也会受影响。
注:当比较两个基本数据类型的值时就是比较值,当比较两个对象时,比较的是他的内存地址,就算两个对象是一样的,但内存地址不一样返回false。
var obj2=new Object();
var obj3=new Object();
obj2.name="xhh"
obj3.name="xhh"
console.log(obj2==obj3)
对象字面量
使用对象字面量来创建一个对象
var obj={}
其中{}就是字面量
可以在字面量里直接添加对象的属性名和属性值。
语法:{属性名:属性值,属性名:属性值,…}
注:属性名可以不加"“号,但如果是使用的特殊名称则要加”"。如:“123”=“小灰灰”。如果一个属性后没有其他属性则不用加,了。
例:
var obj2={
name:"小灰灰",
gender:"男",
age:7
};
console.log( obj2);
属性名后的值可以是对象,读取这个属性时就会显示对象里的属性。
var obj2={
name:"小灰灰",
gender:"男",
age:7,
test:{name:"小灰灰"}
};
console.log( obj2.test);
对象补充知识
- 对象的属性值可以是任何数据类型,也可以是一个函数
ar obj=new Object();
obj.name="小灰灰";
obj.age=12;
obj.han=function(){
console.log(obj.age)
};
console.log(obj.han);
可以去调用这个函数
obj.han();
函数也可称为对象的属性,如果函数作为对象的属性保存则称这个函数是这个对象的方法。调用这个函数就称调用对象的方法,但这只是名称上的区别。
- 枚举对象中属性
用for…in语句
语法:for(var 变量 in 对象){ }
var obj=new Object();
obj.name="小灰灰";
obj.age=12;
obj.gender="男";
for(var n in obj){
console.log(n)
}
这样对象有几个属性,循环体就会执行几次。每次执行就会将属性名赋值给变量。
在后面加一条console.log(“属性值=”+obj[n])语句,则可以读取到他的属性值
var obj=new Object();
obj.name="小灰灰";
obj.age=12;
obj.gender="男";
for(var n in obj){
console.log(n);
console.log("属性值="+obj[n])
}
函数的简介
- 函数(function)也是一个对象
- 函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码)(函数可储存一些代码在需要时调用)。使用typeof检查返回function
- 创建一个函数对象
var fun=new Function()
可以将要封存的代码以字符串的形式传递给构造函数
var fun=new Function(" console.log('笔画');")
封装到函数里的代码不会立即执行,而是会在函数调用时执行,且封装的代码按顺序执行。
调用的语法:函数对象()
注:实际开发中很少使用构造函数来创建一个函数对象
- 使用函数声明创建函数
语法:function 函数名([形参1,形参2,…]){语句}
调用:函数名()
function fun1(){
console.log("笔画");
alert("单位")
}
fun1();
- 函数表达式来创建一个函数
语法: var 函数名=function([形参1,形参2,…]){语句};(创建了一个匿名函数,再将匿名函数赋值给给变量)
[形参1,形参2,…]表示可选的,写不写都行。
函数参数
可以在函数的()里指定一个或多个形参,多个形参间用,来隔开。声明形参相当于在函数内部声明了对应的变量,但不赋值。在调用时在()中指定实参(可是任意数据类型),实参会赋值给函数中对应的形参
function fun(a,b){
var a
var b
console.log(a+b)
}
fun(1,4)
注:调用函数时解析器不会检查实参的类型,要注意是否接收到了非法参数。如果有需要对参数类型进行检查
调用函数时解析器也不会对检查实参数量·,多余的实参不会被复值。无对应实参的形参将是undefined
实参可以是任何值
实参可以是对象
function fun(o){
console.log("我是"+o.name+",今年"+o.age+"岁")
}
var obj={name:"小灰灰",
age:7}
fun(obj);
参数也可以是一个函数
function fun(o){
console.log("我是"+o.name+",今年"+o.age+"岁")
}
var obj={name:"小灰灰",
age:7}
function fun1(a){
a(obj)
}
fun1(fun);
在这里的实参是函数fun,调用a就相当于调用fun是一样效果,因为这里是把fun这个函数对象赋值给了参数a
也可以将匿名函数传给实参如
fun(function(){alert("hi")})
注:fun1(fun())是调用函数,相对于调用函数的返回值。fun1(fun)调用函数对象,相对于直接使用函数对象
函数的返回值
可使用return来设置函数返回值。return后面的语句不执行
语法:return 值
return后的值将会作为函数的执行结果来返回,可定义一个变量来接收执行结果
function fun(a,b){
var d=a+b;
return d;
}
var list=fun(1,4)
console.log("result="+list);
注:return后面不跟任何值就相当于返回了一个undefined,如果函数中不写return,也会返回undefined。return后可以跟任何类型的值
返回值的类型
function fun(){
alert("action");
for(var i=0;i<5;i++){
console.log(i);
}
alert("end");
break:跳出本次循环
continue:跳出当次循环
return:结束本次循环。后面的alert(“end”);就不会执行了
返回值可以是任何值,包括对象
function fun(){
var obj={name:"小灰灰"};
return obj;
}
var a=fun();
console.log("a="+a.name);
也包括一个函数
function fun(){
function fun2(){
alert("嘻嘻");
}
return fun2;
}
var a=fun();
console.log("a="+a);
这时可以调用返回值
function fun(){
function fun2(){
alert("嘻嘻");
}
return fun2;
}
var a=fun();
console.log("a="+a);
a();
将a()换成fun()()是一样的效果。
立即执行函数
函数定义完立即被调用叫立即执行函数,他往往只执行一次。
如下:
(function(a,b){
console.log("a="+a);
console.log("a="+b);
})(1,2);
全局作用域
作用域指一个变量的作用范围。
JS有两种作用域:
- 全局作用域
- 直接编写在script标签中的JS代码都在全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁 - 在全局作用域中都有一个全局对象window
它代表浏览器窗口,由浏览器创建我们可以直接使用 - 全局作用域中
创建的变量都作为window对象的属性储存
创建的函数都做为window对象的方法储存 - 全局作用域中的变量都是全局变量
在页面的任何部分都可以访问到 - 变量的声明提前
用var关键字声明的变量在所有代码执行前就被声明了,在代码执行到它实际位置时才被赋值 - 函数的声明提前
用函数的声明(function 函数(){})创建的函数会在所有代码执行前就被创建,因此调用时无论调用的那条代码在哪里都可以。
而用函数表达式创建的函数没有这种情况.当调用的那条函数提前时会出现以下情形
fun();
var fun=function(){
alert("创造");
}
因为var提前声明了fun,它是一个undefind。调用是相当于调用的undefind它不是一个函数
函数作用域
调用函数时创建函数作用域,执行完后函数作用域销毁。
每调用一次就创建一个新的函数作用域,他们之间相互独立。
在全局作用域里不可以调用函数作用域里的变量,而在函数作用域里可以调用全局作用域里的变量。
在函数作用域里调用一个变量时先看是否在函数里有,没有则向上一级里去找。一直找到全局作用域里面。如果没找到则报错。(如果在函数作用域里还有的情况下找全局作用域里的变量用对象名的方式,如 console.log(“a=”+window.a)
在函数作用域里也有声明提前这种情况:
function fun(){
console.log("a="+a);
var a=2;
}
fun()
这里的var关键字声明的变量在console.log(“a=”+a);语句前就被声明了,只不过没赋值
function fun(){
fun1()
function fun1(){
var a=2;
console.log("a="+a);
}
}
fun();
在函数中,没有用var关键字声明的变量都会成为全局变量。定义形参就相当于在函数作用域里声明了变量
如:
var a=2;
function fun(){
alert(a);
a=3;
}
fun();
alert(a);
调用函数时执行的结果是a=2,而外部alert(a)执行结果是a=3。因为函数中未用var关键字定义的a=3的值就赋值给了全局变量里的a。
this
- 解析器在调用函数时每次都会像函数内部传递一个隐含参数this。
- this指向的是一个对像。这个对象称之为函数执行的上下文对象。
- 根据调用的方式不同,this指向的对象也会不同。
以函数的形式调用时this永远是window
以对象的形式调用时this指向的就是调用方法的对象
function fun(){
console.log(this)
}
fun();
function fun(){
console.log(this.name)
}
var bj={
name:"s0wk",
Sayname:fun
}
bj.Sayname();
用this.name这种方法,根据调用者的不同,name对应不同调用者中name的值
构造函数
可区分多种不同类型的对象
就是一个普通函数创建方式和普通函数没区别,不同的是构造函数习惯首字母大写。
构造函数和普通函数的区别是调用方式的不同,构造函数用new关键字来调用
构造函数的执行流程:
- 立即创建一个新的对象
- 将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象。
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
同一个构造函数创建的对象我们称为一类对象。也将一个构造函数称为一类。将通过一个构造函数创建的对象称为该类的实例。
可以通过instanceof检查一个对象是否属于一个类的实例
语法:对象 instanceof 构造函数
是则返回true,不是则返回false
所有对象都是object的后代
检查任何一个对象用它来和object检查时都会返回true
function Person(a,b,c){
this.name=a;
this.age=b;
this.gender=c
};
var per=new Person("安静",12,"女");
console.log(per);
console.log(per instanceof Person);
构造函数的修改
当构造函数内部中有一个方法,构造函数每执行一次都会创建一个新的方法这些方法是唯一的,但需要实现的效果相同,可以将它提出到全局变量中让所有对象共享同一个方法。
function Person(a,b,c){
this.name=a;
this.age=b;
this.gender=c
this.sayHi=fun;
};
function fun(){
alert("效果="+this.name)
};
var per=new Person("安静",12,"女");
var per1=new Person("小写",2,"女");
per.sayHi();
console.log(per);
console.log(per1);
原型对象
上面那种方法有局限:将函数定义在全局作用域污染了全局作用域的空间,而且不安全,可能重名
原型prototype,我们创建的每一个函数,解析器都会向函数中添加一个属性prototype.这个属性对应着一个对象,这个对象就是我们说的原型对象。
如果函数作为普通函数调用prototype没有任何作用
当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型,我们可通过__proto__来访问如:console.log(mc.__proto__)
原型对象相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果寻不到就会去原型对象中寻找,找到则直接使用(创建构造函数时,可以将这些对象共有的属性和方法同一添加到构造函数的原型中,这样就不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法)
原型对象
创建一个构造函数,向其原形中添加一个属性,用in检查对象中是否含有某个属性时,如果是对象中没有但原型中有,也会返回true
可以用对象的hasOwnProperty()来检查对象自身是否有该属性
自身没有则去原型对象中去找,原型对象也是对象,也有原型。当我们使用一个对象的属性或方法时先在自身寻找,自身没有则去原型对象中找,如果还没有则去原型对象的原型中找,直到找到Object对象的原型。Object对象的原型没有原型,如果这时还没找到则返回undefined
function create(){
}
create.prototype.age=23;
var mc=new create();
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"))