• 前端基础入门之JS对象 原型 prototype


    对象

    JS中数据类型

    • String 字符串
    • Number数值
    • Boolean 布尔值
    • Null空值
    • Undefined 未定义

    以上这五种类型属于基本数据类型,以后我们看到的值只要不是上边的5种,全都是对象

    1、Object 对象

    基本数据类型都是单一的值"hello" 123 true,值和值之间没有任何的联系。

    在JS中来表示一个人的信息(name gender age):

    var name = "孙悟空";
    var gender = "男";
    var age = 18;
    
    • 1
    • 2
    • 3

    如果使用基本数据类型的数据,我们所创建的变量都是独立,不能成为一个整体。

    对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。

    2、对象的分类

    2.1、内建对象

    由ES标准中定义的对象,在任何的ES的实现中都可以使用

    常见内建对象有以下,都可以直接通过new调用构造函数创建对象实例:

    • Object、Function、Array、String、Number、Boolean、Date、RegExp
    • Error(EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError)
    // Math
    Math.sqrt(2);
    // String
    String(2);
    // Number
    Number("2");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2、宿主对象

    由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象

    比如 BOM DOM

    // console
    console.log("hello");
    // document
    document.write("hello");
    
    • 1
    • 2
    • 3
    • 4

    JavaScript实现包括三部分:

    组成作用地位例子
    ES(ECMAScript)描述JS语法和基本对象核心
    DOM(Document Object Model 文档对象模型)HTML和XML的应用程序接口,处理网页内容的方法和接口W3C标准document
    BOM(Browser Object Model 浏览器对象模型)描述与浏览器进行交互的方法和接口,处理浏览器窗口和框架浏览器厂商对DOM的实现window

    DOM

    img

    BOM

    img

    DOM 和 BOM 的关系

    JavaScript的Dom和Bom

    2.3、自定义对象

    由开发人员自己创建的对象

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

    函数使用typeof检查一个对象时,会返回object

    在对象中保存的值称为属性

    • 添加或修改对象属性的语法:对象.属性名=属性值;
    • 读取对象属性的语法:对象.属性名
    • 删除对象属性的语法:delete 对象.属性名;
    var obj = new Object();
    // 向obj中添加一个name属性
    obj.name = "孙悟空";
    // 向obj中添加一个gender属性
    obj.gender = "男";
    // 向obj中添加一个age属性
    obj.age = "18";
    // 打印obj
    console.log(typeof obj); // object
    console.log(obj); // {"age":"18","gender":"男","name":"孙悟空"}
    console.log(obj.name); // 孙悟空
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    属性名

    对象的属性名不强制要求遵守标识符的规范,什么乱七八糟的名字都可以使用,但是我们使用是还是尽量按照标识符的规范去做

    如果要使用特殊的属性名,不能采用.的方式来操作,而需要使用另一种语法:对象["属性名"]=属性值,读取时也需要采用这种方式

    obj["name"] = "齐天大圣";
    console.log(obj["name"]); // 齐天大圣
    
    • 1
    • 2

    使用[]这种形式去操作属性,更加的灵活,在[]中可以直接传递一个变量,这样变量值是哪个就会读取哪个属性

    var n = "nihao";
    obj[n] = "你好";
    console.log(obj[n]); // 你好
    
    • 1
    • 2
    • 3

    回顾.[]new这几个运算符的优先级是最高的

    属性值

    JS对象的属性值,可以是任意的数据类型,包括对象

    var obj2 = new Object();
    obj2.name = "猪八戒";
    obj.bro = obj2;
    console.log(obj.bro.name); // 猪八戒
    
    • 1
    • 2
    • 3
    • 4

    in运算符

    通过该运算符可以检查一个对象中是否含有指定的属性

    如果有则返回true,没有则返回false

    语法:"属性名" in 对象

    console.log("test" in obj); // false
    console.log("name" in obj); // true
    
    • 1
    • 2

    3、基本数据类型和引用数据类型

    基本数据类型 String Number Boolean Null Undefined

    引用数据类型 Object

    基本数据类型

    • JS中的变量都是保存到栈内存中的,基本数据类型的值直接在栈内存中存储
    • 值与值之间是独立存在,修改一个变量不会影响其他的变量
    var a = 1;
    var b = a;
    console.log("a=" + a + ", b=" + b); // a=1, b=1
    b = 2;
    console.log("a=" + a + ", b=" + b); // a=1, b=2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    引用数据类型

    • 对象是保存到堆内存中的
    • 每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用)
    • 如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响
    var obj3 = obj;
    obj3.name = "斗战胜佛";
    console.log(obj.name);  // 斗战胜佛
    console.log(obj3.name); // 斗战胜佛
    
    • 1
    • 2
    • 3
    • 4

    比较

    • 当比较两个基本数据类型的值时,就是比较值。
    • 而比较两个引用数据类型时,它是比较的对象的内存地址,如果两个对象是一摸一样的,但是地址不同,它也会返回false
    var o1 = new Object();
    var o2 = new Object();
    o1["name"] = "周瑜";
    o2["name"] = "周瑜";
    console.log(o1 == o2); // false
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4、对象字面量

    使用对象字面量,可以在创建对象时,直接指定对象属性的语法:{属性名: 属性值, 属性名: 属性值...}

    对象字面量的属性名可以加引号也可以不加(建议不加),如果要使用一些特殊的名字,则必须加引号

    属性名和属性值是一组一组的名值对结构,名和值之间使用:连接,多个名值对之间使用,隔开

    如果一个属性之后没有其他的属性了,就不要写,

    var obj = {
        name: "孙悟空",
        age: 1000,
        gender: "男",
        bor:{
            name: "猪八戒"
        }
    }
    console.log(obj); // {"age":1000,"bor":{"name":"猪八戒"},"gender":"男","name":"孙悟空"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、方法

    对象的属性值可以是任何的数据类型,也可以是个函数(下一节知识)

    函数也可以称为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法

    调用函数就说调用对象的方法,但是它只是名称上的区别没有其他的区别

    var obj2 = {
        name: "猪八戒",
        age: 18,
        sayName: function() {
            console.log(obj2.name);
        }
    };
    obj2.sayName(); // 猪八戒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6、枚举对象中的属性

    使用for...in语句语法:

    for(var 变量 in 对象) {
    	语句...
    }
    
    • 1
    • 2
    • 3

    for...in语句对象中有几个属性,循环体就会执行几次

    每次执行时,会将对象中的一个属性的名字赋值给变量

    var obj = {
        name: "孙悟空",
        age: 1000,
        gender: "男",
        address: "花果山"
    };
    for(var key in obj){
        console.log(key + "=" + obj.key);
        // name=undefined
        // age=undefined
        // gender=undefined
        // address=undefined
        console.log(key + "=" + obj[key]);
        // name=孙悟空
        // age=1000
        // gender=男
        // address=花果山
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    作用域

    作用域指一个变量的作用的范围

    在JS中一共有两种作用域:

    • 全局作用域
    • 函数作用域

    7、全局作用域

    直接编写在script标签中的JS代码,都在全局作用域

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

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

    在全局作用域中:

    • 创建的变量都会作为window对象的属性保存
    • 创建的函数都会作为window对象的方法保存

    全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到

    var a = 3;
    console.log(window.a); //3
    console.log(a); //3
    
    b = 3;
    console.log(b); //3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7.1、变量的声明提前

    使用var关键字声明的变量,会在所有的代码执行之前被声明

    但是如果声明变量时不适用var关键字,则变量不会被声明提前

    // 1、变量的声明提前
    console.log("a = " + a); // a = undefined
    var a = "abc";
    // ======相当于======
    var a;
    console.log("a = " + a); // a = undefined
    a = "abc";
    
    // 2、没有变量的声明提前,报错
    console.log("b = " + b); // UncaughtReferenceError: b is not defined
    b = "abc";
    // ======相当于======
    console.log("b = " + b); // UncaughtReferenceError: b is not defined
    window.b = "abc";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    7.2、函数的声明提前

    使用函数声明形式创建的函数function

    函数(){
    	语句...
    }
    
    • 1
    • 2
    • 3

    它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数

    fun1(); // fun1...
    fun2(); // UncaughtTypeError: fun2 is not a function
    // 函数声明,会被提前创建
    function fun1(){
        console.log("fun1...");
    }
    // 函数表达式,不会被提前创建(变量会被提前声明,但函数不会被提前创建)
    var fun2 = function(){
        console.log("fun2...");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    8、函数作用域

    调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁

    每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的

    • 在函数作用域中可以访问到全局作用域的变量
    • 在全局作用域中无法访问到函数作用域的变量

    当在函数作用域操作一个变量时,它会先在自身作用域中寻找,

    • 如果有就直接使用
    • 如果没有则向上一级作用域中寻找,直到找到全局作用域
    • 如果全局作用域中依然没有找到,则会报错

    在函数中要访问全局变量可以使用window对象

    var a = 10;
    function fun2(){
        var a = 20;
    
        function fun3(){
            var a = 30;
            console.log("fun3 ==> a = " + a);  // fun3 ==> a = 30
        }
    
        fun3();
    
        console.log("fun2 ==>a = " + a); // fun2 ==>a = 20
        console.log("a = " + window.a); // a = 10
    }
    fun2(); 
    console.log("a = " + a); // a = 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在函数作用域也有声明提前的特性,使用var关键字声明的变量,会在函数中所有的代码执行之前被声明

    函数声明也会在函数中所有的代码执行之前执行

    // 在函数作用域也有声明提前的特性,使用`var`关键字声明的变量,会在函数中所有的代码执行之前被声明
    function func1(){
        console.log(a);
        var a = "func1";
    
        // 函数声明也会在函数中所有的代码执行之前执行
        func2(); // fun2...
        function func2(){
            console.log("fun2...");
        }
    }
    func1(); // undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在函数中,不适用var声明的变量都会成为全局变量

    // 函数声明且调用
    func3();
    function func3() {
        a = 4;
    }
    console.log("a = " + window.a);  // a = 4
    console.log("a = " + window["a"]);   // a = 4
    console.log("a = " + a);    // a = 4
    // 函数声明不调用
    function func4() {
        b = 4;
    }
    console.log("b = " + window.b);  // b = 4
    console.log("b = " + window["b"]);   // b = 4
    console.log("b = " + b);    // UncaughtReferenceError: b is not defined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    定义形参就相当于在函数作用域中声明了变量

    var e = 10;
    function fun5(e){
        console.log(e);
    }
    fun5(); // undefined
    fun5(55);  // 55
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    练习

    // 说出以下代码的执行结果
    var a = 123; 
    function fun(){
        console.log(a);
    }
    fun(); 			// 123
    // =====================
    var a = 123; 
    function fun(){
        console.log(a);
        var a = 456;
    }
    fun(); 			// undefined
    console.log(a);  // 123
    // =====================
    var a = 123; 
    function fun(){
        console.log(a);
        a = 456;
    }
    fun(); 			// 123
    console.log(a);  // 456
    // =====================
    var a = 123; 
    function fun(a){
        console.log(a);
        a = 456;
    }
    fun();			// undefined
    console.log(a);  // 123
    // =====================
    var a = 123; 
    function fun(a){
        console.log(a);
        a = 456;
    }
    fun(789);		// 789
    console.log(a);  // 123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    9、this

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

    this指向的是一个对象,这个对象我们称为函数执行的上下文对象

    根据函数的调用方式的不同,this会指向不同的对象

    • 以函数的形式调用时,this永远都是window
    • 以方法的形式调用时,this就是调用方法的那个对象
    // - 以函数的形式调用时,`this`永远都是`window`
    function fun(){
        console.log(this.name);
    }
    var name = "ddd"; // ddd
    fun();
    // - 以方法的形式调用时,`this`就是调用方法的那个对象
    var obj = {
        name: "孙悟空",
        sayName: fun
    }
    obj.sayName(); // 孙悟空
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    构造函数与原型对象

    10、使用工厂方法创建对象

    function createPerson(name, age, gender){
        // 创建一个新的对象
        var obj=new Object();
        //向对象中添加属性
        obj.name = name;
        obj.age = age;
        obj.gender = gender;
        obj.sayName = function(){
            console.log(this.name);
        };
        //将新的对象返回
        return obj;
    }
    
    var obj1 = createPerson("孙悟空", 1000, "男");
    var obj2 = createPerson("猪八戒", 3600, "男");
    var obj3 = createPerson("沙悟净", 10000, "男");
    
    obj1.sayName(); // 孙悟空
    obj2.sayName(); // 猪八戒
    obj3.sayName(); // 猪八戒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    使用工厂方法创建的对象,使用的构造函数都是Object

    所以创建的对象都是Object这个类型,就导致我们无法区分出多种不同类型的对象

    11、构造函数

    创建一个构造函数,专门用来创建Person对象的构造函数就是一个普通的函数

    创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写构造函数

    和普通函数的区别就是调用方式的不同

    • 普通函数是直接调用
    • 构造函数需要使用new关键字来调用
    function Person(){
        console.log(this); // Person{}
    }
    // 普通函数
    var fun = Person();
    console.log(fun); // undefined
    // 构造函数
    var person = new Person();
    console.log(person); // Person{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    构造函数的执行流程

    1. 立刻创建一个新的对象
    2. 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
    3. 逐行执行函数中的代码
    4. 将新建的对象作为返回值返回
    function Dog(){
    
    }
    
    function Person(name, age, gender){
        //向对象中添加属性
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.sayHello = function(){
            console.log("My'name is " + this.name + ", " +
                        "I'm " + this.age + " years old, " +
                        "and I'm a " + this.gender + ".");
        };
    }
    
    var person1 = new Person("孙悟空", 1000, "man");
    var person2 = new Person("猪八戒", 3600, "man");
    var person3 = new Person("沙悟净", 10000, "man");
    var dog = new Dog();
    person1.sayHello(); // My'name is 孙悟空, I'm 1000 years old, and I'm a man.
    person2.sayHello(); // My'name is 猪八戒, I'm 3600 years old, and I'm a man.
    person3.sayHello(); // My'name is 沙悟净, I'm 10000 years old, and I'm a man.
    console.log(person1); // Person {name: "孙悟空", age: 1000, gender: "man", sayHello: ƒ}
    console.log(person2); // Person {name: "猪八戒", age: 3600, gender: "man", sayHello: ƒ}
    console.log(person3); // Person {name: "沙悟净", age: 10000, gender: "man", sayHello: ƒ}
    console.log(typeof person1); // object
    console.log(typeof person2); // object
    console.log(typeof person3); // object
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。

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

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

    如果是则返回true,否则返回false

    console.log(person1 instanceof Person); //true
    console.log(person2 instanceof Person); //true
    console.log(person3 instanceof Person); //true
    console.log(dog instanceof Person); 	//false
    
    • 1
    • 2
    • 3
    • 4

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

    console.log(person1 instanceof Object); //true
    console.log(person2 instanceof Object); //true
    console.log(person3 instanceof Object); //true
    console.log(dog instanceof Object); 	//true
    
    • 1
    • 2
    • 3
    • 4

    this的情况:

    • 当以函数的形式调用时,thiswindow
    • 当以方法的形式调用时,谁调用方法this就是谁
    • 当以构造函数的形式调用时,this就是新创建的那个对象

    构造函数修改

    创建一个Person构造函数

    在Person构造函数中,为每一个对象都添加了一个sayName方法,目前我们的方法是在构造函数内部创建的

    也就是构造函数每执行一次就会创建一个新的sayName方法也是所有实例的sayName都是唯一的

    function Person(name, age, gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.sayHello = function(){
            console.log("My'name is " + this.name + ", " +
                        "I'm " + this.age + " years old, " +
                        "and I'm a " + this.gender + ".");
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一模一样的

    这是完全没有必要,完全可以使所有的对象共享同一个方法

    function Person(name, age, gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.sayHello = fun;
    }
    // 将sayName方法在全局作用域中定义
    function fun(){
        console.log("My'name is " + this.name + ", " +
                    "I'm " + this.age + " years old, " +
                    "and I'm a " + this.gender + ".");
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    将函数定义在全局作用域,虽然节省了空间,但却污染了全局作用域的命名空间

    而且定义在全局作用域中也很不安全

    12、原型对象

    原型prototype

    我们所创建的每一个函数(不论是普通函数还是构造函数),解析器都会向函数中添加一个属性prototype

    function Person(){
    
    }
    
    function MyClass(){
    
    }
    
    console.log(Person.prototype);
    // {constructor: ƒ}
    // 		constructor: ƒ Person()
    // 			arguments: null
    // 			caller: null
    // 			length: 0
    // 			name: "Person"
    // 			prototype: {constructor: ƒ}
    // 			__proto__: ƒ ()
    // 			[[FunctionLocation]]: 09-原型对象.html:8
    // 			[[Scopes]]: Scopes[1]
    // 		__proto__: Object
    console.log(Person.prototype == MyClass.prototype); // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    当函数以普通函数的形式调用prototype时,没有任何作用

    当函数以构造函数的形式调用prototype时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性

    var mc1 = new MyClass();
    var mc2 = new MyClass();
    var mc3 = new MyClass();
    console.log(mc1.__proto__ == MyClass.prototype); // true
    console.log(mc2.__proto__ == MyClass.prototype); // true
    console.log(mc3.__proto__ == MyClass.prototype); // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20210727225124094

    原型对象就相当于一个公共区域,所有同一个类的实例都可以访问到这个原型对象

    我们可以将对象中共有的内容,统一设置到原型对象中

    // 向MyClass中添加属性a
    MyClass.prototype.a = "123";
    console.log(mc1.a);  // 123
    // 向MyClass中添加方法sayHello
    MyClass.prototype.sayHello = function(){
    alert("hello");
    }
    mc3.sayHello();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    mc2.a = "456";
    console.log(mc2.a);  // 456
    
    • 1
    • 2

    以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中

    这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了

    hasOwnProperty

    function MyClass(){
    
    }
    MyClass.prototype.name = "I'm prototype's name.";
    var mc = new MyClass();
    mc.age = 18;
    // 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
    console.log("name" in mc); // true
    console.log("age" in mc); // true
    // 可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
    // 使用该方法只有当对象自身中含有属性时,才会返回true
    console.log(mc.hasOwnProperty("name")); // false
    console.log(mc.hasOwnProperty("age"));  // true
    console.log(mc.hasOwnProperty("hasOwnProperty"));  // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    那么,hasOwnProperty是原型对象中定义的方法吗?

    因为对象中没有定义hasOwnProperty方法,那应该就是在原型对象中定义的了,果真如此吗?

    我们用hasOwnProperty方法看下有没有hasOwnProperty它自己

    console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));  // false
    
    • 1

    我们发现,原型对象中也没有hasOwnProperty方法,那hasOwnProperty究竟是哪里来的呢?

    原型的原型

    原型对象也是对象,所以它也有原型,当我们使用一个对象的属性或方法时

    • 会先在自身中寻找,自身中如果有则直接使用
    • 如果没有则去原型对象中寻找,有则使用
    • 如果没有则去原型的原型中寻找,直到找到Object对象的原型
    • Object对象的原型没有原型,如果在Object中依然没有找到,则返回undefined
    ```javascript
    console.log(mc.helloWorld);  // undefined
    ```
    
    • 1
    • 2
    • 3

    image-20210727231924585

    那么,按照这个原理,我们在原型的原型中使用hasOwnProperty方法看看

    console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));  // true
    
    • 1

    那既然原型对象有原型,那原型的原型还有原型吗?

    话不多说,直接打印看下

    console.log(mc.__proto__.__proto__.__proto__);  // null
    
    • 1

    根据上述原理,mc.__proto__.__proto__就是Object对象了

    Object对象虽然没有原型,但也有__proto__,只是为null而已

    toString

    当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值

    如果我们希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法

    function Person(name, age, gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    var per1 = new Person("孙悟空", 1000, "man");
    var per2 = new Person("猪八戒", 3600, "man");
    // 当我们直接在页面中打印一个对象时,事件上是输出的对象的`toString()`方法的返回值
    console.log(per1); // Person {name: "孙悟空", age: 1000, gender: "man"}
    console.log(per1.toString()); // [object Object]
    // 如果我们希望在输出对象时不输出`[object Object]`,可以为对象添加一个`toString()`方法
    per1.toString = function(){
        return "Person[name=" + this.name + ", age=" + this.age + ", gender=" + this.gender + "]";
    }
    console.log(per1); // Person {name: "孙悟空", age: 1000, gender: "man", toString: ƒ}
    console.log(per1.toString()); // Person[name=孙悟空, age=1000, gender=man]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    上述只是修改per1对象的toString方法,不会对其他对象产生影响

    如果想要所有对象都执行该方法,可以修改Person原型的toString

    console.log(per2.toString()); // [object Object]
    // 修改Person原型的toString
    Person.prototype.toString = function(){
        return "Person[name=" + this.name + ", age=" + this.age + ", gender=" + this.gender + "]";
    }
    console.log(per2.toString()); // Person[name=猪八戒, age=3600, gender=man] 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    13、垃圾回收(GC)

    就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾这些垃圾积攒过多以后,会导致程序运行的速度过慢

    所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾

    image-20210728191835732

    当一个对象没有任何的变量或属性对它进行引用,我们将永远无法操作该对象

    此时这种对象就是一个垃圾,这种对算过多会占用大量的内存空间,导致程序运行变慢

    image-20210728192324257

    在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作

    我们需要做的只是要将不再使用的对象设置null即可

    var obj = new Object();
    // ...
    obj = null
    
    • 1
    • 2
    • 3
  • 相关阅读:
    一种信息系统免疫安全防护架构
    287. 寻找重复数
    1048 Find Coins
    Subset Sum 问题单个物品重量限制前提下的更优算法
    GUI+SQLServer考试系统
    微信小程序中基础入门
    STM32概述
    超越 Transformer开启高效开放语言模型的新篇章
    哪些不得不记下的汇编指令
    我的期末网页设计HTML作品——咖啡文化网页制作
  • 原文地址:https://blog.csdn.net/agonie201218/article/details/125400698