2. 对象
对象是JS中的引用数据类型,是一种复合数据类型,在对象中可以保存多个不同数据类型的属性,使用typeof
检查一个对象时,会返回object 。
2.1 对象的分类
-
内建对象
- 由ES标准中定义的对象,在任何的ES的实现中都可以使用
- 比如:Math String Number Boolean Function Object….
-
宿主对象
- 由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
- 比如:BOM DOM
-
自定义对象
- 开发人员自己创建的对象
- 创建对象
var obj = new Object(); //堆内存开辟空间 var obj2 = {}; //两种创建方式
-
向对象中添加属性
语法:对象.属性名 = 属性值
或者对象[“属性名”] = 属性值
这种能使用特殊属性名 -
删除对象中的属性
delete 对象.属性名 delete 对象["属性名"]
2.2 遍历
- 使用
in
检查对象中是否含有指定属性,语法:"属性名" in 对象
,如果对象中含有该属性,则返回true
;否则,返回false
。
//循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
var obj = {
name : "yyl",
height : "189cm",
age : 21,
1 : 'a'
} //使用特殊属性名,要加"" ,如"@#$serf%^^"
for(var i in obj) {
console.log(i,":",obj[i]);
} //for语句,obj有多少个属性循环多少次。每次执行时,会把对象中的一个属性赋值给变量
- 基本数据类型的数据,变量是直接保存的它的值。变量与变量之间是互相独立的,修改一个变量不会影响其他的变量。
- 引用数据类型的数据,变量的保存是对象的引用(内存地址)。如果多个变量指向的是同一个对象,此时修改一个变量的属性,会影响其他的变量。
- 比较两个变量时,对于基本数据类型,比较的就是值;对于引用数据类型比较的是地址,地址相同才相同。
2.3 函数
函数也是一个对象,也具有普通对象的功能(能有属性);函数中可以封装一些代码,在需要的时候可以去调用函数来执行这些代码;使用typeof检查一个函数时会返回function。
2.3.1 基础知识
- 创建函数
var fun = new Function{"console.log('hhhhh')"};
- 函数声明
function 函数名([形参1,形参2...形参N]){
语句...
}
- 函数表达式
var 函数名 = function([形参1,形参2...形参N]){
语句...
};
//匿名函数赋值
2.3.2 立即执行函数
函数定义完,立即被调用,这种函数叫做立即执行函数。立即执行函数往往只会执行一次。
(
function (a,b){
console.log("sum = ",a+b);
}
)(123,456);
2.3.3 形参和实参
- 形参:定义函数时,可以在()中定义一个或多个形参,形参之间使用
,
隔开。定义形参就相当于在函数内声明了对应的变量但是并不赋值,形参会在调用时才赋值。 - 实参:调用函数时,可以在()传递实参,传递的实参会赋值给对应的形参。调用函数时JS解析器不会检查实参的类型和个数,可以传递任意数据类型的值。如果实参的数量大于形参,多余实参将不会赋值;如果实参的数量小于形参,则没有对应实参的形参将会赋值undefined。
- 返回值return。如果return后不跟值,或者是不写return则函数默认返回undefined。
2.3.4 方法(method)
可以将一个函数设置为一个对象的属性,当一个对象的属性是一个函数时,我们称这个函数是该对象的方法。
var obj = {
name : "hhh",
age : 12,
sayName : function(){
document.write(obj.name);
}
}
function fun(){
console.log(obj.name);
}
//调方法
obj.sayName();
//调函数
fun();
2.3.5 函数的方法call和apply
call()
和apply()
这两个方法都是函数对象的方法,需要通过函数对象来调用;- 通过两个方法可以直接调用函数,并且可以通过第一个实参来指定函数中this;
- 不同的是call是直接传递函数的实参,而apply需要将实参封装到一个数组中传递。
function fun(a,b){
console.log("a = "+a);
console.log("b = "+b);
console.log(this);
}
var obj = {
name:"hhhh",
age:12,
sayName:function(){
console.log(this.name);
}
}
var obj2 = {
name:"tttt",
age:15,
sayName:function(){
console.log(this.name);
}
}
fun(0,0); //this是windos
fun.call(obj,1,2); //this是obj
fun.apply(obj2,[3,4]); //this是obj2,apply实参封装成数组
obj.sayName(); //输出“hhhh”
obj.sayName.call(obj2); //输出“tttt”,因为this被指定为boj2
obj2.sayName(); //输出“tttt”
obj2.sayName.apply(obj); //输出“hhhh”,this被指定为obj
2.3.6 函数的属性arguments
- arguments和this类似,都是函数中的隐含的参数
- arguments是一个类数组元素(不是数组),它用来封装函数执行过程中的实参,所以即使不定义形参,也可以通过arguments来使用实参
- arguments中有一个属性callee表示当前执行的函数对象
//arguments的三个重点:length、callee、类数组
function fun1(a,b){
//console.log("hhhh");
console.log(arguments.callee); //fun1
console.log(arguments.length);
console.log("a = ",arguments[0]);
console.log(arguments instanceof Array);
//输出false
}
fun1(1,2); //length输出2,a = 1
fun1(); //length输出0,a = undefined
//______分割_______
function fun2(){
console.log(arguments.length);
console.log("a = ",arguments[0]);
console.log("b = ",arguments[1]);
}
fun2(2,3);
//输出length为2,a = 2,b = 3
//不定义形参,通过arguments来使用实参
2.4 作用域
2.4.1 作用域
- 全局作用域:
- 直接在script标签中编写的代码都运行在全局作用域中
- 全局作用域在打开页面时创建,在页面关闭时销毁
- 全局作用域中有一个全局对象window,window对象由浏览器提供,可以在页面中直接使用,它代表的是整个的浏览器的窗口
- 在全局作用域中创建的变量都会作为window对象的属性保存
- 在全局作用域中创建的函数都会作为window对象的方法保存
- 在全局作用域中创建的变量和函数可以在页面的任意位置访问
- 在函数作用域中也可以访问到全局作用域的变量
- 尽量不要在全局中创建变量
var a = 10;
var b = 111;
function fun1(){
var b = "hhhh";
document.write("a = "+a+"<br />");
document.write("b = "+b+"<br />");
document.write("window.b = ",window.b,"<br />");
}
console.log("a = ",window.a);
window.fun1();
- 函数作用域
- 函数作用域是函数执行时创建的作用域,每次调用函数都会创建一个新的函数作用域
- 函数作用域在函数执行时创建,在函数执行结束时销毁
- 在函数作用域中创建的变量,不能在全局中访问
- 【就近原则】当在函数作用域中使用一个变量时,它会先在自身作用域中寻找,如果找到了则直接使用,如果没有找到则到上一级作用域中寻找,如果找到了则使用,找不到则继续向上找。
2.4.2 声明提前
- 变量的声明提前
- 在全局作用域中,使用var关键字声明的变量会在所有的代码执行之前被声明,但是不会赋值。
- 所以我们可以在变量声明前使用变量。但是不使用var关键字声明的变量不会被声明提前。
- 在函数作用域中,也具有该特性,使用var关键字声明的变量会在函数所有的代码执行前被声明。
- 如果没有使用var关键字声明变量,则变量会变成全局变量。
console.log(a);
var a = 10;
//报:undefined
//等同于下面
var a;
console.log(a);
a = 10;
//but
console.log(a);
a = 10;//报错误,ReferenceError
//4的情况
var b = 111;
function fun(){
b = 777;
}
fun(); //调用后,函数内的b是全局,所以全局b值修改;如果不调用执行后,输出111
console.log(b);
//输出777
- 函数的提前声明
- 在全局作用域中,使用函数声明创建的函数
function fun(){}
,会在所有的代码执行之前被创建,也就是我们可以在函数声明前去调用函数,但是使用函数表达式var fun = function(){}
创建的函数没有该特性。
- 在全局作用域中,使用函数声明创建的函数
fun();
function fun(){
console.log("hhhh");
}
//正常执行
//but
fun();
var fun = function(){
console.log("ddd");
}
//报错
2.5 this(上下文对象)
- 解析器在调用函数时,每次都会向函数内部传递一个隐含的参数,即this。
- this的指向是一个对象,这个对象我们称为函数执行的上下文对象。
- 根据函数调用方式不同,this会指向不同的对象
- 以函数形式调用时,this是window
- 以方法的形式调用时,this就是调用方法的对象
- 以构造函数的形式调用时,this就是新创建的对象
- 使用
call
和apply
调用时,this是指定的那个对象
function fun(){
console.log(this.name);
}
var obj = {
name:"hhhh",
sayName:fun
}
var name = "全局";
fun(); //this是window,输出“全局”
obj.sayName(); //this是调用方法的对象,即obj;输出“hhhh”
2.6 构造函数
- 构造函数是专门用来创建对象的函数,一个构造函数我们也可以称为一个类。
- 通过一个构造函数创建的对象,我们称该对象是这个构造函数的实例。通过同一个构造函数创建的对象,我们称为一类对象。
- 构造函数就是一个普通的函数,只是他的调用方式不同,如果直接调用,它就是一个普通函数;如果使用
new
来调用,则它就是一个构造函数。
//构造函数
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
console.log(this.name);
};
}
var obj = new Person("sss",12,"male");
//重点用new调用
-
构造函数的流程
- 创建一个新的对象
- 将新的对象作为函数的上下文对象(this)
- 执行函数中的代码
- 将新建的对象返回
-
instanceof
用来检查一个对象是否是一个类的实例- 语法:
对象 instanceof 构造函数
- 如果该对象是构造函数的实例,则返回true,否则返回false
- Object是所有对象的祖先,所以任何对象和Object做instanceof都会返回true
- 但是
null instanceof Object
返回false
。因为null的类型是object,但是null不具有任何对象的特性,就是说我们并不能执行null.toString()、null.constructor等对象实例的默认调用。
- 语法:
2.7 原型(prototype)
-
创建一个函数以后,解析器都会默认在函数中添加一个数prototype,prototype属性指向的是一个对象,这个对象我们称为原型对象。
-
当函数作为构造函数使用,它所创建的对象中都会有一个隐含的属性执行该原型对象。
-
这个隐含的属性可以通过对象.__proto__来访问
-
原型对象就相当于一个公共的区域,凡是通过同一个构造函数创建的对象他们通常都可以访问到相同的原型对象
-
我们可以将对象中共有的属性和方法统一添加到原型对象中,这样我们只需要添加一次,就可以使所有的对象都可以使用
-
当我们去访问对象的一个属性或调用对象的一个方法时,它会先自身中寻找,
- 如果在自身中找到了,则直接使用;
- 如果没有找到,则去原型对象中寻找,如果找到了则使用;
- 如果没有找到,则去原型的原型中寻找,依此类推。直到找到Object的原型为止,Object的原型的原型为null;
- 如果依然没有找到则返回undefined。
-
hasOwnProperty()
这个方法可以用来检查对象自身中是否含有某个属性(不会像第6点一样进行追溯),语法:对象.hasOwnProperty(“属性名”)
2.8 toString方法
- 当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
- 如果我们希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法
- 谷歌和火狐在此问题上有不同
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
//修改Person原型的toString
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
};
var per = new Person("hhh",12,"male");
var result = per.toString();
console.log("result = "+result);
console.log(per);
//代码运行后,谷歌以及火狐对上述代码的显示如下
-
谷歌运行后
-
火狐运行后
2.9 垃圾回收(GC)
- 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
- 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作
- 我们需要做的只是要将不再使用的对象设置null即可