个人自学前端19-JS12-类和原型和原型链
类和原型和原型链
一 类
面向对象里,有一个非常基础的概念,就是类。
类就是拥有相同特征的一类对象的集合。
我们已经接触过的 js 原生类有很多。
例如:Array,Date,Object等。
类是对象的模板,对象是类的实例。
为什么需要使用类?它有什么用?(很抽象)
类可以归类多个数据,方便编程时获取.
方便给多个对象添加相同的方法.
现实生活中,其实类的影子比比皆是.
比如我们说鸟,这里鸟是一个类.
说到麻雀鸟,这是鸟类的一个对象,一个实例.
这里有一个很有意思的哲学问题,可能可以帮助你理解什么是类.
请思考,下面的命题是否是正确的.
1:世界上没有马。
2:世界上没有水果。
3:世界上没有人。
以上命题在哲学角度,都是对的。
如果世界上有马,请牵一匹马过来,你一定牵不来一匹马,你牵来的一定是白马或者黑马。
如果世界上有水果,请拿一个水果来,你一定拿不来水果,你拿来的一定是香蕉或者苹果之类。
如果世界上有人,请找来一个人,你一定找不来一个人,你找来的一定是男人或者女人.
以上说的马,水果,人,的确是不存在的,之所以人们能知道马的样子,是因为看过了很多的马总结出来的。
这里马,水果,人就是类,白马黑马是马这个类的对象。苹果香蕉是水果类的对象。男人女人是人类的对象。
类都是抽象的,看不见的,对象都是看得见的,摸得着的。
js 中的Array就是一个类,它表示所有的数组这个集合。它是看不见的,抽象的。
我们能看得见的是一个个的数组实例。例如:[1,2,3],[4,5,6] 等等.
我们认知中所有的数组,构成了一个看不见的抽象的数组类.
类的两个概念:类的私有属性,类的公有方法。
人是一个类,每个人的名字各不一样,则名字就是类的私有属性。
每个人都会说话,这个行为每个人都是一致的,这个应该是类的公有方法。
数组是一个类,每个数组的length各不相同,则length就是数组的私有属性。
每个数组实例都可以通过push方法把一个元素放到数组的最后面,这个push应该是数组类的公有方法。
是人都会说话,只要你是人就会说话。
数组都可以push,只要你是数组,就可以push。
类有归纳总结的作用。
那如何给一个类添加一些自定义的公有方法呢?
二 原型
类的原型:类实例共同特征的集合。(类的公有方法的集合)
所有的人的共同特征:会说话,会穿衣,会使用工具等等。
人所有的这些共同特征的集合就构成了人类的原型。
以数组为例:数组都可以push,pop,shift,unshift,splice等,则所有这些方法,就构成了数组类的原型。
// 原型其实就是类的一个属性。
console.log(Array.prototype);
// 数组原型的数据类型是对象。这个对象上的所有属性,就是所有数组的公有方法。所有的数组都可以使用。
console.log(typeof Array.prototype); // object
这个原型有什么用?可以通过这个原型,给所有的数组实例添加一些自定义的公有方法。
// 给原型对象添加一个fn方法,则所有的数组实例都可以使用这个方法。
Array.prototype.fn = function(){console.log(100)};
[1,2,3].fn();
[4,5,6].fn();
公有属性存储在哪里 => 存储在原型对象上.
系统默认的方法,没有prototype属性.
自定义的方法有prototype属性.有prototype属性意味着fn可以实现一个自定义类.(面向对象)
如何获取一个对象的上一级原型对象
let oDiv = document.getElementById('wrap');
console.log(oDiv.__proto__);
// 如果我想给所有的div标签都添加一个公有方法.
// oDiv
// => HTMLDivElement.prototype
// => HTMLElement.prototype
// => Element.prototype
// => Node.prototype
// => EventTarget.prototype
// => Object.prototype
三 原型链
3.1 继承
龙生龙,凤生凤,老鼠生儿会打洞。这其实就是一种继承现象。
北海龙王继承龙类。黑凤继承凤类。锦毛鼠继承鼠类。
所有数组实例默认都可以push,这也是一种继承现象。[1,2,3]继承Array。
js 的继承有点奇葩,实例不是继承类,而是继承类的原型。
对于某个实例,可以通过属性proto来访问它继承的是什么对象.
console.log([1,2,3].__proto__);
console.log([1,2,3].__proto__ == Array.prototype); // true
数组继承数组的原型,以此类推.
所有的函数组成一个类Function,因此所有的数组继承Function.prototype;
所有的纯对象组成一个类Object,因此所有的纯对象继承Object.prototype;
继续推理:
那 Array.prototype 继承什么呢?
Array.prototype 本身就是一个纯对象,因此 Array.prototype 是一个纯对象实例,它默认继承 Object.prototype
console.log(Array.prototype.__proto__ == Object.prototype); // true
梳理一下:
[1,2,3] 继承 Array.prototype,Array.prototype 继承 Object.prototype。这类似于 子 => 父 => 祖父的关系. 即:
[1,2,3] => Array.prototype => Object.prototype ;
这种关系,组成了一个 "链条",这种关系,称之为 "原型链"。(有点类似作用域链)
原型链描述的就是一种继承上的关系。
[1,2,3]继承Array.prototype,可以访问Array.prototype上的方法。
Array.prototype 继承 Object.prototype,可以访问 Object.prototype 上的方法。
则 [1,2,3] 实际上也继承 Object.prototype,也可以访问 Object.prototype上的方法。
Object.prototype.fn = function(){console.log(200)};
[1,2,3].fn();
[4,5,6].fn();
知道了数组的原型链,可以推论出其他数据类型的原型链。
函数原型链:alert => Function.prototype => Object.prototype
纯对象原型链: {} => Object.prototype
通过分析这些原型链,可以得出结论,在Object.prototype上添加的方法,数组,函数,纯对象都可以访问。
Object.prototype.fn = function(){console.log(200)};
[1,2,3].fn();
alert.fn();
window.fn();
这种关系有点类型于:
生物类
动物类
哺乳类
鸟类
鱼类
植物类
松科
生物类的特征是需要氧气,因此所有的子类都需要氧气。
比较一下:
Object.prototype
Array.prototype
[1,2,3],[4,5,6]
Function.prototype
alert,parseInt
Object.prototype 拥有fn方法,因此所有的子类和子类实例都可以访问。
所有原型链的顶层对象都是 Object.prototype (null).
任何类的实例,都可以访问Object.prototype的方法.
四 私有属性
属性可以分为私有属性和公有属性
1:私有属性.(自定义属性)(手动添加)(存储在对象身上)
2:公有属性(继承属性)(不手动添加,默认就有的属性)(不存储在对象身上)
方法也分私有和公有
1:私有方法(自定义,手动添加的方法)
2:公有方法(继承方法)(默认就有的方法,默认就能使用的方法)
如何判断一个属性是不是私有的.
hasOwnProperty => 判断是不是私有属性的.
1: 用来干嘛 => 判断一个属性是不是某个对象的私有属性
2: 参数就是要检测的属性名
3: 返回布尔值
4: 对象.hasOwnProperty(属性名);
const obj = { name: '幂幂' };
// 判断name属性是不是obj的私有属性。
console.log(obj.hasOwnProperty('name'));
// 判断toString属性是不是obj的私有属性。
console.log(obj.hasOwnProperty('toString'));
// in => 可以检查一个对象能不能访问某个属性(方法)
if (!obj.hasOwnProperty('toString') && 'toString' in obj) {
alert('toString是公有方法')
}
五 原型链与作用域链
作用域链 => 非面向对象 => 确定某个作用域内能访问到的变量,作用域链确定了,能访问到的变量也就确定了.
原型链 => 面向对象 => 确定某个实例能访问到的属性(方法),原型链确定了,能访问到的属性(方法)就确定.
作用域链 => 实现变量查找
1:先写除作用域链.
2:沿着作用域链查找,查找变量声明,找到就停止查找.(就近原则).找到全局作用域,如果还没有就报错.
原型链 => 实现属性查找
1:先写原型链.
2:沿着原型链查找,查找当前的原型对象有没有对应的私有属性,有就停止查找(就近原则),找到Object.protorype,如果还没有,得到undefined.
let arr = [1,2,3];
console.log(arr.abc);
// arr => Array.prototype => Object.prototype
// 1: 先看看arr有没有abc私有属性.如果有就是它,停止往上查找
// 2: 如果arr没有abc私有属性,看看Array.prototype有没有abc私有属性.如果有就是它,停止往上查找
// 3: 如果Array.prototype没有abc私有属性,看看Object.prototype有没有abc私有属性.如果有就是它.没有就返回undefined
// in => 检查对应的属性是不是存在于实例的原型链上.
let obj = {name: '幂幂'};
Object.prototype.age = 32;
// for in 可以检测变量实例原型链上的可枚举的属性.
// 可枚举 => 可for in.
for (let key in obj) {
console.log(key);
}
本文来自博客园,作者:暗鸦08,转载请注明原文链接:https://www.cnblogs.com/DarkCrow/p/15045026.html

浙公网安备 33010602011771号