05-原型与原型链

6/22/2022

# 1. 公有属性和私有属性

公有属性和私有属性

  • 一切都是对象

  • 对象是属性的无序集合

  • 每一个对象上都有一个叫_ _ proto _ _这样的属性,是一个属性名

  • _ _ proto _ _对应的属性值是一个对象,这个对象叫隐式原型对象

  • 代码如下:

<script>
    // 对象是属性的无序集合
    // 属性分两类:1)私有属性  2)公有属性
    // 私有属性指的是对象自己的属性
    let obj = {
        name: "wc", // 私有属性
        age: 18 // 私有属性
    }
    console.dir(obj);
</script>
1
2
3
4
5
6
7
8
9
10
<script>
    // 数组也是对象
    // 对象是属性的无序集合
    // 每一个对象上都有一个叫__proto__的属性
    // __proto__属性对应的值是一个对象
    let arr = ["wc", "xq"];
    console.dir(arr);
</script>
1
2
3
4
5
6
7
8

# 2. 每一个对象都有一个__proto__属性

2,每一个对象都有一个__proto__属性

<script>
    // 每一个对象都有一个叫__proto__的属性,它的值是一个对象
    // 这个对象叫隐式原形对象
    let obj = {
        name: "wc", // 私有属性
        age: 18 // 私有属性
    }
    console.dir(obj);
    // 现在去obj中找hasOwnProperty  查找顺序如下:
    // 1)先在自己的私有属性中找  如果找到了 就使用 如果找不到 
    // 2)这会沿着__proto__去它的隐式原形对象中找  如果找到了  就使用  如果找不到
    // 3)还会沿着__proto__去它的隐式原形对象的隐式原形对象中
    console.log(obj.hasOwnProperty("name")); // hasOwnProperty 判断一个属性是否属于自己的私有属性
    console.log(obj.hasOwnProperty("hasOwnProperty"));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 看一个题:
<script>
    let obj = {
        name: "wc",
        age: 18
    }
    console.log(obj.__proto__); // 得到obj的隐式原型对象
</script>
1
2
3
4
5
6
7
<script>
    let obj = {
        name: "wc",
        age: 18
    }
    console.log(obj.__proto__.hasOwnProperty("hasOwnProperty")); // true
</script>
1
2
3
4
5
6
7
<script>
    let arr = ["wc", "xq"];
    console.dir(arr)
    // arr要找push  
    //  1)先去自己私有属性中找push  找不到
    //  2)沿着__proto__去隐式原型对象中
    arr.push("z3")
</script>
1
2
3
4
5
6
7
8
<script>
    let arr = ["wc", "xq"];
    console.dir(arr)
    // arr要找valueOf
    //  1)先去自己私有属性中找valueOf  找不到
    //  2)沿着__proto__去隐式原型对象中  找不到
    //  3)再沿着__proto__去arr的隐藏原型对象的隐式原型对象中找
    arr.valueOf("z3")
</script>
1
2
3
4
5
6
7
8
9
<script>
    let arr = ["wc", "xq"];
    console.dir(arr)
    // arr要找abc
    //  1)先去自己私有属性中找valueOf  找不到
    //  2)沿着__proto__去隐式原型对象中  找不到
    //  3)再沿着__proto__去arr的隐藏原型对象的隐式原型对象中找
    //  4)再沿着__proto__去arr的隐式原型对象的隐式原型对象的隐式原型对象中找
    //  5)....  直到找到null  如果还找不到,就报错
    arr.abc();
</script>
1
2
3
4
5
6
7
8
9
10
11
  • 补充一个运算符:in
<script>
    let arr = ["wc"];
    // 判断push是否是arr对象的私有属性
    console.log(arr.hasOwnProperty("push"));

    // in是一个运算符  判断一个属性是否是某个对象的属性
    // 这个属性不管是私有的还公有的
    console.log("push" in arr);
</script>
1
2
3
4
5
6
7
8
9

# 3. 每一个函数都有一个prototype属性

  • 作用域链:在EC中查找数据的机制。

  • 原型链:在对象中查找属性的机制。

  • 代码如下:

<script>
    // 每一个函数都有一个prototye属性名
    // 这个属性名对应的值是一个对象,叫原型对象
    // 不要忘了:每一个对象都有一个叫__proto__属性
    // 函数是对象吗?是  函数也有一个__proto__属性
    function fn() {
        console.log("fn..");
    }
    console.dir(fn)

    // Array 叫构造器  构造器也是函数   构造器也是类
    // 函数在JS中是一等公民,函数有多种角色
    // new Array(); 
    console.dir(Array);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 一个函数(构造器)的prototype属性指向的对象 和 通过这个函数(构造器)创建出来的对象的__proto__所指向的是同一个对象,只不过,__proto__指向的一般叫隐式原型对象,prototype指向的,一般叫原型对象。题目如下:
<script>
    let arr1 = new Array("wc", "xq")
    let arr2 = new Array("z3", "L4")
    console.dir(arr1)
    console.log(Array.prototype == arr1.__proto__); // true
    console.log(Array.prototype == arr2.__proto__); // true

    console.log(arr1.__proto__.__proto__ == Object.prototype); // true
    console.log(arr1.__proto__.__proto__.__proto__); // null
</script>
1
2
3
4
5
6
7
8
9
10

# 4. constructor

每一个原型对象上都有一个叫constructor属性,指向它对应的构造器

<script>
    let arr = new Array("wc", "xq");
    // constructor指向Array
    console.log(arr.__proto__.constructor);

    console.log(arr.__proto__.constructor == Array);
</script>
1
2
3
4
5
6
7
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    let p = new Person("wc", 18)
    console.log(p.__proto__.constructor == Person);
</script>
1
2
3
4
5
6
7
8
<script>
    let num = new Number(110)
    console.log(num.__proto__.constructor == Number);
</script>
1
2
3
4

# 5. 原型链的题

需要记住的几个概念:

  • 一切都是对象
  • 函数有多种角色
  • 对象是属性的无序集合
  • 每一个对象上都有一个__proto__属性
  • 每一个函数上都有一个prototype属性
  • 原型链研究的是对象属性的查找机制
  • 作用链研究的是EC中数据的查找机制
  • 闭包一个没有被销毁的执行上下文(栈空间)
  • new做了什么,如下:
    (1)在构造器内部创建一个新的对象
    (2)这个对象内部的prototype属性会被赋值为该构造函数的prototype属性;
    (3)让构造器中的this指向这个对象
    (4)执行构造器中的代码
    (5)如果构造器没有返回对象,则返回上面的创建出来的对象
<script>
    function Fn() {
        this.x = 10;
        this.y = 20;
        this.getX = function() {
            console.log(this.x)
        }
    }
    Fn.prototype.getX = function() {
        console.log(this.x)
    }
    Fn.prototype.getY = function() {
        console.log(this.y)
    }
    let f1 = new Fn();
    f1.getX();
    f1.getY();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
    function Fn(num) {
        this.x = this.y = num;
    }
    Fn.prototype = {
        x: 20,
        sum: function() {
            console.log(this.x + this.y)
        }
    }
    Fn.prototype.constructor = Fn;
    let f1 = new Fn(10)
    console.log(f1.sum === Fn.prototype.sum)
    f1.sum();
    Fn.prototype.sum();
    console.log(f1.constructor)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
    function Fn() {
        let a = 1;
        this.a = a;
    }
    Fn.prototype.say = function() {
        this.a = 2;
    }
    Fn.prototype = new Fn();
    let f1 = new Fn();
    Fn.prototype.b = function() {
        this.a = 3;
    }
    console.log(f1.a)
    f1.say();
    console.log(f1.a)
    f1.b();
    console.log(f1.a)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    function Fn() {
        this.x = 10;
        this.y = 20;
    }
    Fn.n = 1000;
    Fn.say = function() {
        console.log("hello")
    }
    Fn.prototype.sum = function() {
        return this.x + this.y;
    }
    let f1 = new Fn();
    f1.sum();
    console.log(f1.sum());
    console.log(Fn.prototype == Fn.__proto__);
    Fn.sum();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
    function C1(name) {
        if (name) {
            this.name = name;
        }
    }

    function C2(name) {
        this.name = name;
    }

    function C3(name) {
        this.name = name || "Hello";
    }
    C1.prototype.name = "Tom"
    C2.prototype.name = "Tom"
    C3.prototype.name = "Tom"
    console.log((new C1().name) + (new C2().name) + (new C3().name))
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6. 再次聊一下创建对象的几种方式

1)普通方式(new Object 或 字面量)

<script>
    let coder = new Object();
    coder.color = "white"
    coder.start = function() {
        console.log("开始打代码~");
    }
</script>
<!-- 不足:不方便批量地创建对象 -->
1
2
3
4
5
6
7
8

2)工厂模式创建对象

<script>
    function createCoder() {
        let coder = new Object();
        coder.color = "white"
        coder.start = function() {
            console.log("开始打代码~");
        }
        return coder;
    }

    let coder1 = createCoder();
    let coder2 = createCoder();
    let coder3 = createCoder();
</script>
<!-- 不足:每个对象都是Object的实例。 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

3)构造器(构造函数)创建对象

<script>
    function Coder(color) {
        this.color = color;
        this.start = function() {
            console.log("开始打代码~");
        }
    }
    let redCoder = new Coder("red");
    let blueCoder = new Coder("blue");
</script>
<!-- 不足:造成内存空间浪费 -->
1
2
3
4
5
6
7
8
9
10
11

4)构造器+原型创建对象

  • 把公有属性放到原型上就OK了,代码如下:
<script>
    function Coder(color) {
        this.color = color; // 每一个程序员的私有属性
    }

    Coder.prototype.start = function() {
        console.log("开始打代码~");
    }

    let redCoder = new Coder("red");
    let blueCoder = new Coder("blue");
</script>
1
2
3
4
5
6
7
8
9
10
11
12

# 7. 静态属性

静态属性是绑定在类的属性,类是构造器,构造器是函数,函数也是对象。

  • 代码如下:
<script>
    function Person(name) {
        this.name = name; // 对象自己的私有属性

        // Person叫构造器 类 构造函数
        // 函数有多种角色  对象
        Person.total = 110; // total是们于Person这个类的
        // 位于类上的属性,叫静态属性
    }
    let wc = new Person("wc")
    let xq = new Person("xq")
    console.log(wc.name);
    console.log(xq.name);

    // 访问静态属性必须通过类名
    console.log(Person.total);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Last Updated: 12/25/2022, 10:02:14 PM