class(类)和构造函数(原型对象)
构造函数和class的关系,还有面向对象和原型对象,其实很多人都会很困惑这些概念,这是第二次总结这些概念了,之前一次,没有class类,其实了解了构造函数,class也就很容易理解了
一. 构造函数和原型
1.function 声明(创造)了一个函数 Person,叫构造函数
2.原型对象:在声明了一个函数之后,浏览器会自动按照一定的规则创建一个对象,这个对象就叫做原型对象。这个原型对象其实是储存在了内存当中。
3.在声明了一个函数后,这个构造函数(声明了的函数)中会有一个属性prototype,这个属性指向的就是这个构造函数(声明了的函数)对应的原型对象;原型对象中有一个属性constructor,这个属性指向的是这个构造函数(声明了的函数)。
所以说:Person(构造函数)的 prototype 指向原型对象,原型对象的 constructor 属性,又指向该构造函数本身
1 function Person(name, age) { 2 this.name = name; 3 this.age = age; 4 } 5 console.log(Person); // 构造函数本身 6 console.log(Person.prototype.constructor); // 指向 构造函数本身 7 Person("范顺", 18);
二. 构造函数使用new来创建对象
1 function students() { 2 3 } 4 5 var stu = new students();
1. stu 是 new students()(构造函数) 创建出来的对象,这个stu对象中是没有prototype属性的,prototype属性只有在构造函数students中有
2. 但stu(创建出来的对象)有一个__proto__属性,stu调用这个属性可以直接访问到构造函数students的原型对象(也就是说,stu的__proto__属性指向的是构造函数的原型对象)
所以:stu.__proto__ = students.prototype 都指向原型对象
3. 我们给原型对象添加属性和方法,那么 new 出来的这个对象就可以访问到该原型对象的属性和方法
1 function students() { 2 /* 我就是构造函数 */ 3 } 4 students.prototype.name="shun" 5 var stu = new students(); 6 console.log(stu.__proto__.name); // 访问到原型对象添加的属性 7 console.log(stu.name); // 这样也是能访问到原型对象添加的属性,因为stu本身没有这个name属性,所以说会向stu对象的__proto__ 属性指向的原型对象中找,找到就返回,找不到就往上找,就是原型链 8 9 stu.name = "fan" // 给stu对象添加属性 10 console.log(stu.name); // 访问stu对象的属性
4. 因为 new 出来的 stu 是一个对象,我们也可以给它直接设置属性,如果找到直接返回值,如果stu对象本身没有这个属性,那么就会向上找stu对象的__proto__属性指向的原型对象中查找,如果查找到则返回。(如果原型中也没有找到,则继续向上找原型的原型 这就是所说的原型链。
5. 如果通过stu对象添加了一个属性name,则stu对象来说就屏蔽了原型中的属性name。 换句话说:在stu中就没有办法访问到原型的属性name了。
通过stu对象只能读取原型中的属性name的值,而不能修改原型中的属性name的值。 stu.name = “李四”; 并不是修改了原型中的值,而是在stu对象中给添加了一个属性name
<script type="text/javascript"> function students () { } // 可以使用students.prototype 直接访问到原型对象 //给students函数的原型对象中添加一个属性 name并且值是 "张三" students.prototype.name = "张三"; students.prototype.age = 20; var stu = new students(); /* 访问stu对象的属性name,虽然在stu对象中我们并没有明确的添加属性name,但是 stu的__proto__属性指向的原型中有name属性,所以这个地方可以访问到属性name 就值。 注意:这个时候不能通过stu对象删除name属性,因为只能删除在stu中删除的对象。 */ alert(stu.name); // 张三 var stu1 = new students(); alert(stu1.name); // 张三 都是从原型中找到的,所以一样。 alert(stu.name === stu1.name); // true // 由于不能修改原型中的值,则这种方法就直接在stu中添加了一个新的属性name,然后在stu中无法再访问到 //原型中的属性。 stu.name = "李四"; alert(stu.name); //李四 // 由于stu1中没有name属性,则对stu1来说仍然是访问的原型中的属性。 alert(stu1.name); // 张三 </script>
三. 与原型有关的几个方法
**1. prototype属性**
prototype 存在于构造函数中 (其实任意函数中都有,只是不是构造函数的时候prototype我们不关注而已) ,他指向了这个构造函数的原型对象。
**2.constructor属性**
constructor属性存在于原型对象中,他指向了构造函数
如下面代码:
<script type="text/javascript"> function students () { } alert(students.prototype.constructor === students); // true </script>
我们根据需要,可以students.prototype 属性指定新的对象,来作为students的原型对象。但是这个时候有个问题,新的对象的constructor属性则不再指向students构造函数了。
**3.__proto__ 属性(注意:左右各是2个下划线)**
用构造方法创建一个新的对象之后,这个对象中默认会有一个属性__proto__, 这个属性就指向了构造方法的原型对象。
四. class(类)
1 class Point { 2 constructor() { 3 // ... 4 } 5 toString() { 6 return '类似于够赞函数中的原型链,访问到这个原型身上的方法' 7 } 8 toValue() { 9 // ... 10 } 11 } 12 13 14 // 等同于 15 // Point.prototype = { 16 //toString(){}, 17 //toValue(){} 18 //}; 19 20 // 所以说 21 const p1 = new Point() 22 console.log(p1.toString()) //类似于够赞函数中的原型链,访问到这个原型身上的方法
实例属性和静态属性(实例方法和静态方法)
这里我是进入了一个误区,但是通过打印发现
拿下面的例子举例:我错误的认为 P1.info 是有值的,虽然构造函数身上追加了一个属性 info,显而易见,这是原型对象的 constructor 身上的属性,并不是原型对象身上的属性
,因为 原型对象的 constructor指向构造函数本身,这个info是构造函数身上的方法,而通过new出来的实例对象 P1.info 其实是通过原型链的形式访问原型对象身上的info属性,所以说访问的是 undefined,这里的 constructor属性和 P1 的 info属性都属于原型对象身上的属性,他俩是平级关系
只要是通过new出来的实例能访问到的方法(属性),叫做实例方法,静态方法只存在构造函数本身
实例属性:通过new出来的实例访问到的属性叫做实例属性
静态属性:在构造函数中构造函数添加的属性 或者说 在class内部通过static 修饰的属性叫做静态属性
1 function Person(name, age) { 2 this.name = name; 3 this.age = age; 4 } 5
Person.show = function() { //静态方法
console.log('这是静态方法')
} 6 Person.info = '哈哈' //静态属性
Person.prototype.say = function() { //这是实例方法,挂载到的是构造函数的原型对象身上,P1可以访问到
console.log("这是实例方法,挂载到的是构造函数的原型对象身上,P1可以访问到")
} 7 const P1 = new Person("范顺", 18); 8 console.log(P1.name) //通过new出来的实例访问到的属性叫做实例属性 9 console.log(P1.age) 10 11 console.log(P1.info) // undefined 12 console.log(Person.info) //静态属性
P1.say() //这是实例方法,挂载到的是构造函数的原型对象身上,P1可以访问到
13 14 15 class Anmin { 16 constructor(name, age) { 17 this.name = name; 18 this.age = age; 19 } 20 21 // 在class内部通过static 修饰的属性叫做静态属性,只能被构造函数名或者类名读取,new出来的对象读不到 22 static info = '哈哈'
say() {
console.log('实例方法')
}
static show() {
console.log('静态方法')
}
23 } 24 25 const A1 = new Anmin('大黄', 9) 26 27 console.log(A1.name) //实例属性 28 console.log(A1.age) 29 30 console.log(A1.info) // undefined 31 console.log(Anmin.info) //静态属性
console.log(A1.say()) //实例方法 say
class类的extends继承
继承父类的属性和方法
1 class Father { 2 constructor(name, age) { 3 this.name = name 4 this.age = age 5 } 6 } 7 // 子类 8 class Person extends Father { 9 constructor(name, age, yellow) { //yellow 是子类定义的属性 10 super(name, age, yellow) 11 this.yellow = yellow 12 } 13 } 14 15 const P1 = new Person("范顺", 18, "红色") 16 console.log(P1) 17 18 // 子类 19 // class Anmainl { 20 // } 21 22 // const A1 = new Anmainl('二哈', 9) 23 // console.log(A1)