JavaScript对象

发布于:2025-06-30 ⋅ 阅读:(15) ⋅ 点赞:(0)

对象分类

  1. 内建对象

    由ES标准中定义的对象,在任何的ES的实现中都可以使用。比如Math、Date、基本数据类型对象等

  2. 宿主对象

    由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象。比如:BOM、DOM

  3. 自定义对象

    有开发人员自己创建的对象。

对象的基本操作

创建对象

使用new关键字调用的函数是构造函数,构造函数是专门用来创建对象的函数。

var obj = new Object();
console.log(obj)

使用typeof 可以检查一个变量是否是对象类型。

向对象添加属性:对象.属性名 = 属性值

obj.name = "孙悟空";

读取对象中属性值:对象.属性名。如果读取的属性对象中没有,不会报错而是返回undefined

修改对象的属性值:对象.属性名 = 新属性值

删除对象的属性delete 对象.属性名

delete obj.name; //这样就删除了对象的name属性
属性名和属性值

属性名命名规范:不强制要求遵循标识符的规范,想用什么就用什么(如果要使用特殊的属性名,不能采用.的方式来操作,需要采用对象["属性名"] = 属性值方式操作,读的时候也需要对象["属性名"]方式读取),但是在实际开发中还是遵循标识符的规范。

一般使用[]这种方式操作属性更加灵活,因为在[]中可以直接传递一个变量。用.不可以直接传递变量

var obj = new Object();
obj.name = "小明";
obj["789"] = "111";
console.log(obj);
var num = "789";
console.log(obj[num]);

JS属性值可以是任意的数据类型。使用in运算符可以检查一个对象中是否含有指定的属性。返回值为true/false;

console.log("name" in obj); //true
console.log("789" in obj); //true
console.log("test" in obj); //false
基本数据类型和引用数据类型
基本数据类型

String Number Boolean Null Undefined

引用数据类型

Object array function

字面量

在创建对象时,也会经常使用这样方式{}大括号方式创建对象。

var obj = {属性名1:"属性值1", 属性名2:"属性值2", ... ,  属性名n:"属性值n"}; 

这两种创建对象的方式都是一样的,属性名可以加引号也可以不加,建议加引号。如果一个属性之后没有属性了,就不要写逗号了。

方法

对象的属性值可以是任何数据类型,也可以是函数,这样的函数称之为这个对象的方法。

//定义一个方法2
obj.sayName = function(){
    console.log(obj.name);
}
//定义一个方法2
obj1 = {
    name:"小明",
    sayName:function(){
    	console.log(obj.name);
	}
} 
//调用方法
obj.sayName();
obj1.sayName();

枚举对象中的属性

使用for ... in, 语法:

for(var n in 对象){
   //... 
}

for...in语句对象中有几个属性,循环体就会执行几次。每次执行时,会将对象中的一个属性的名字赋值给变量。取值时可以使用obj[n]。

作用域

作用域指一个变量的作用的范围。在JS中一共有两种作用域:

  1. 全局作用域

    直接编写在script标签中的JS代码, 并未写在函数、对象中的变量都在全局作用域。

    全局作用域在页面打开时创建,在页面关闭时销毁。

    在全局作用域中有一个全局对象window, 它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用。在全局作用域中

    • 创建的变量都会作为window对象的属性保存。
    • 创建的函数都会作为window对象的方法保存。
      全局作用域中的变量都是全局变量,在当前页面的任意的部分都可以访问的到。
  2. 函数作用域
    调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁。
    每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。
    在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。

    变量访问的就近原则:当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用。如果没有则向上一级作用域中寻找,直到找到全局作用域。如果全局作用域中依然没有找到,则会报错ReferenceError。在函数中要访问全局变量可以使用window对象。

    var a = 10;
    function fun(){
    	var a ="我是fun函数中的变量a";
        var b = 20;
        // console.log( "a = "+a);
        function fun2(){
        	console.1og( "a = "+window.a); //使用全局中的a
        }
    	fun2();
    }
    fun();
    

声明提前

变量的声明提前
console.log("a ="+a); //a undefined
var a = 123;
function fun1(){ //函数中变量也会声明提前到函数顶行
	console.log("b ="+b); //b undefined
    var b = 456;
};
fun1();

上面代码不会报错,只会警告a是undefined。

变量的声明提前:使用var关键字声明的变量,会在所有的代码执行之前被声明(不会被赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前。

函数的声明提前

使用函数声明形式创建的函数function 函数 (){ ... }, 它会在所有的代码执行之前最先被创建。所以我们可以在函数声明前来调用函数。

使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用。

fun1(); //正常调用, 因为fun1()是函数声明,转化为window.fun1。window为全局变量,所以可以调用。
fun2(); //报错:fun2变量使用了提前变量声明,但是没有被赋值为一个函数类型,调用fun2()报错。
//函数声明
function fun( ){
	console.log("我是一个fun函数");
};
//函数表达式
var fun2 = function(){ 
	console.log("我是fun2函数");
};

function fun1(){ //函数中函数也会声明提前到函数顶行
    fun3();
	function fun3(){
        console.log("正常不正常?");
    }
};
fun1();

函数在执行过程中都会先看自己作用域中是否有var声明的变量,如果有,检查是否执行变量提前声明操作。如果没有,使用就近原则找上层。

this

解析器在调用函数时每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this。

这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象(谁调用方法,this就指向谁)。

function fun(){
    console.log(this);
}
var obj = {
	name:"孙悟空",
    sayName:fun
};
fun() //这里相当于是window.fun() 所以打印是window对象
obj.sayName() //obj对象
  1. 以函数的形式调用时,this永远都是window
  2. 以方法的形式调用时,this就是调用方法的那个对象
  3. 以构造函数的形式调用时,this就是新创建的那个对象
function func(){
    console.log(this.name)
};
var obj1 = {
    name:"悟空",
    sayName:func
};
var obj2 = {
    name:"八戒",
    sayName:func
};
obj1.sayName();
obj2.sayName();

使用工厂方法创建对象

function createPerson(name, age, gender){
    var obj = {
        name:name,
        age:age,
        gender:gender,
        sayName:function(){
            console.log(this.name);
        }
    }
    return obj;
}
var xmf = createPerson("小明他爸", "45", "男");
var xmm = createPerson("小明他妈", "40", "女");
var xm = createPerson("小明", "10", "男");
xmf.sayName();
xmm.sayName();
xm.sayName();

构造函数

使用工厂方法创建的对象使用的构造函数都是Object(),所以创建的对象都是Object这个类型,就导致我们无法区分出多种不同类型的对象。

创建一个构造函数,专门用来创建Person对象的。构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。

构造函数和普通函数的区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用.

function Person(name, age, gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = function(){
        console.log(this.name);
    }
    return this;
}
var xm = new Person("小明", "10", "男");
xm.sayName();
console.log(xm);

构造函数的执行流程:

  1. 立刻创建一个新的对象
  2. 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
  3. 逐行执行函数中的代码
  4. 将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。

使用instanceof可以检查一个对象是否是一个类的实例语法:

对象 instanceof 构造函数
如果是:则返回true,  否则返回false

所有的对象都是Object的后代,所以任何对象和Object使用instanceof检查时都会返回true。

修改构造函数

创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,目前我们的方法是在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法。也就是说所有实例的sayName都是唯一的。那么如果构造方法执行10000次就会创建10000个新的方法,而10000个方法都是一样的。

解决方法:构造函数中的方法提取到全局作用域中。但是这样将会污染了全局作用域的命名空间(不同同时存在两个叫sayName名字的方法),并且不安全(多人开发中,同名的函数下面的方法会覆盖上面的方法)。所以就用到了下面的原型

原型(prototype)

首先明白一点:函数也是对象。

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype。这个属性对应着一个对象,这个对象就是我们所谓的原型对象。

如果函数作为普通函数调用prototype没有任何作用。当函数以构造函数的形式调用时(var n = new Person()),它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性。
在这里插入图片描述
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

function Person(){
}
var p1 = new Person();
p1.name = "p1";
console.log(Person.prototype); //原型对象
console.log(Person.prototype == p1.__proto__); //true 这两个属性指向的对象是一样的, 都是原型对象
var p2 = new Person();
p2.name = "p2";
console.log(Person.prototype == p2.__proto__); //true 这两个属性指向的对象是一样的, 都是原型对象
Person.prototype.a = "原型对象中的属性a";
p1.a = "p1对象中的a"
console.log(p1.a); //p1对象中的a,这里会先到p1对象中找a属性,然后到原型中找a属性。
console.log(p2.a); //原型对象中的属性a

p2.__proto__.sayName = function(){
    console.log(this.name); //this指的是当前对象的名称
}
p1.sayName(); //p1
p2.sayName(); //p2

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。

以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。

使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true。可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,返回值也是bool类型。

hasOwnProperty方法的来源

原型对象也是对象,所以它也有原型。当我们使用一个对象的属性或方法时,会现在自身中寻找,自身中如果有,则直接使用,如果没有则去原型对象中寻找,如果原型对象中有,则使用,如果没有则去原型的原型中寻找,直到找到Object对象的原型。Object对象的原型没有原型,如果在Object的原型中依然没有找到,则返回undefined.

在这里插入图片描述
Object对象的原型没有原型,所以Object.__proto____proto__ = null

toString

当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值。如果我们希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法。

function Person(name, age){
    this.age = age;
    this.name = name;
}
var p1 = new Person("悟空", 15);
console.log(p1); //[object Object]
var res = p1.toString();
console.log(res);//[object Object]
console.log(p1.hasOwnProperty("toString")); //false
console.log(p1.__proto__.hasOwnProperty("toString")); //false
console.log(p1.__proto__.__proto__.hasOwnProperty("toString")); //false

Person.prototype.toString = function(){ //重写Object对象的__proto__的toString方法
    return "name = " + this.name + ", age="+this.age;
}
console.log(p1.toString());

垃圾回收(GC)

当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。在这里插入图片描述
在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作。我们需要做的只是要将不再使用的对象设置null即可。


网站公告

今日签到

点亮在社区的每一天
去签到