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
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
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
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
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
<script>
let num = new Number(110)
console.log(num.__proto__.constructor == Number);
</script>
1
2
3
4
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
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17