这篇文章中的内容会比较的多,而且在基础中是数据相对比较复杂的基础,主要是讲到了 JS 这门语言中如何实现继承、多态,以及什么情况如何定义 私有属性、方法,共有属性、方法,被保护的属性和方法。明确的定义了 JS 中的访问边界问题,以及最终实现的原理是什么。接下来,让我们仔细瞅瞅这部分吧~
首先在 ES5 中是没有类的概念的,我们一般是通过构造函数中来实现类。下面就举个例子。
另外我们再复习下我们在 JS 中的经常会提到的问题,原型以及原型链
JS 中通过
__prpto__
的桥梁实现原型链, 也叫做实现继承。
JS 中通过prototype
的属性复制自己的模版对象(也可以叫做被复制的对象)
上例子之前,我们来看一张图 ( 来自 juejin,侵删)
造物主无中生有,从 null 制造了一个 No.1 对象( 神 ),这个 No.1 对象觉得自己太孤独,就 copy 了一份自己,我们叫她 Object ,同时 No1 对象 希望 Object 可以为自己干活,然后 Object 就学会了 new 这个技能,new 一下,同时加入各种属性,就可以 瞬间让这个世界丰富多彩了起来,后来,丰富多彩的世界也物以群分了,然后就出现了 String、Number、Boolean、Array、Date... 等等类型(demo1),然后造物主又发现,
String.constructor;
// ? String() { [native code] }
Number.constructor;
// ? Number() { [native code] }
Array.constructor;
// ? Array() { [native code] }
Object.constructor;
// ? Object() { [native code] }
Function.constructor;
// ? Function() { [native code] }
// demo1
String.prototype;
// String {"", constructor: ?, anchor: ?, big: ?, blink: ?, …}
Number.prototype;
// Number {0, constructor: ?, toExponential: ?, toFixed: ?, toPrecision: ?, …}
Array.prototype;
// [constructor: ?, concat: ?, copyWithin: ?, fill: ?, find: ?, …]
Function.prototype;
// ? () { [native code] }
Object.prototype;
// {constructor: ?, __defineGetter__: ?, __defineSetter__: ?, hasOwnProperty: ?, __lookupGetter__: ?, …}
其实到这里的时候,其实很多同学,已经开始彻底蒙圈了,这是啥啊? 怎么一会 constructor 一会 prototype 还有一个 proto 啊啊啊,简直要疯掉了,到底这些都是一些啥啊。。。不要着急,下面我们重新来认识一下这三个货,出现的原因以及分别代表着什么
测试
一、首先,我们总最容易理解的
constructor
(构造器)来理解
var F = function() {
this.name = "F-构造函数";
};
var f1 = new F();
var f2 = new F();
console.log(F.constructor); // ? Function() { [native code] } 是浏览器自带的原生方法 Function
console.log(f1.constructor); // ? () {this.name = ‘F-构造函数‘;} 是构造函数 F 本身
console.log(f2.constructor); // ? () {this.name = ‘F-构造函数‘;} 是构造函数 F 本身
// 这个时候大家其实对于 constructor 属性有一定的了解了,对象、函数都有 constructor 属性
这里我们有一张图
同时,JS 原生自带的一些方法和 上文中 我们定义的 Person 类也非常类似,唯一的区别就是,Person 是用户自己定义的, 生自带的方法是官方指定的。
JS 语言中自带的原生方法 String、Number、Array、Boolean、Function、Object、Date 等,都是 Function 的实例化对象
String、Number、Array、Boolean、Function、Object、Date 等 的 constructor 都指向 Function
(Function 的构造函数也指向 Function,不要疑惑,虽然毫无道理,但是就是这么发生了)
二、接下来我们再来看看
prototype
是怎么样产生的, JS 的语言设计,为什么需要prototype
对象。
var F = function() {
this.name = "F-构造函数";
};
var f1 = new F();
var f2 = new F();
f1.say = function() {
console.log("say hello");
};
f2.say = function() {
console.log("say hello");
};
console.log(f1.say === f2.say); // false
prototype
就应运而生了基于上面的例子我们再修改下:
var F = function() {
this.name = "F-构造函数";
};
F.prototype.say = function() {
console.log("say hello");
};
var f1 = new F();
var f2 = new F();
f1.say(); // say hello
f2.say(); // say hello
console.log(f1.say === f2.say); // true
所以
prototype
对象的出现,达到了 共享、公用的效果。节约了内存。同时prototype
对象用于放某同一类型实例的共享属性和方法,实质上是为了内存着想。
这里我们再放一张图
三、
constructor
属性具体在哪里?
constructor
属性,那会是在哪里呢?constructor
属性都是指向 构造函数(Person)constructor
想必也会占用大量的内存,而且根本没有必要constructor
作为一个共享数据,放在 prototype
对象中,节约内存。var F = function() {
this.name = "F-构造函数";
};
F.prototype.say = function() {
console.log("say hello");
};
var f1 = new F();
var f2 = new F();
f1.constructor = function() {
this.name = "匿名构造函数";
};
console.log(f1.constructor == f2.constructor); // fasle 这个时候 f1 对象就没办法找到自己的构造函数了,
// 因为我们给 f1 实例化对象新增了一个 constructor 属性,这个时候,JS 就会优先返回这个值,而不是真正的 构造函数对象,聪明的 JS 肯定不会让这种事情发生的,对么。下面就该我们的 __proto__ 出场啦
四、
__proto__
的出现 目的: 让实例找到自己的实例
__proto__
__proto__
属性都指向其 构造函数的 prototype
(我们可以把 prototype
理解成一个 可以抽离成成千上万实例化对象都具备的 公共属性的集合 其中包括了:constructor
属性、以及使用者定义在 prototype
上的属性或者方法 )f1.__proto__ === F.prototype; // true
五、总结
实现一个类的话上面的案例基本上简单的呈现了下:
function Person(name) {
this.name = name;
this.run = function() {
console.log(this.name + "跑步");
};
}
Person.prototype.age = 12;
Person.prototype.work = function() {
console.log(this.name + "写代码");
};
Person.weight = "70kg";
Person.eat = function() {
console.log("在吃饭");
};
var p = new Person("zhangsan");
p.run(); // zhangsan跑步
p.work(); // zhangsan写代码
p.eat(); // Uncaught TypeError: p.eat is not a function
// new 操作背后的真相
function New(name) {
this.name = name;
}
// 一、创建一个新的对象
var o = {};
// 二、需要认祖归宗,需要知道自己是被哪个构造函数实例化生成的
o.__proto__ = New.prototype;
// 三、需要拿到 祖上传给你的传家宝
New.apply(o, arguments); // arguments 为传入的参数, 通过执行构造函数,巧妙的将构造函数中 this 的上下文转换成了 新生成的 o 对象的上下文,让其也拥有了构建函数内部的属性和方法
// 四、最后返回 o
return o;
__proto__
指向的是 Person.prototype 刚好,我们在 Person.prototype 新增了一个 work 方法,所以 p 可以通过 __proto__
原型链找到 work 方法执行成功 ...
Person.eat = function() { console.log(‘在吃饭‘) }
p2 = new Person(‘lisi‘)
// 因为 eat 这个静态方法是挂载在构造函数这个对象上的,而我们的 new 操作是继承了 构造函数内部的方法和属性,
// 所以在继承父类私有属性的时候没有找到,那还有 原型链上的呢?同样,new 操作是将 `__proto__` 指向了 Person.prototype 而这个对象中也没有这个方法,所以就报错了
// 那如果 p2 想访问,有办法么? 有的
p2.constructor.eat() // 在吃饭
// 同时 p2.constructor.eat() === Person.eat() === Person.prototype.constructor.eat()
// 但是这种访问的方式,没办法和对象的上下文结合起来,也没有多大的作用,所以我们往往在我们日常的开发中用到的比较少。
对,没错。这一章 都是基础知识,那么基于这个基础上,我们下一章节会正式来进入 typescript 中 class 的学习中来
包括了 TypeScript 中的类,类的定义、方法属性的定义和类的修饰符等,敬请期待~
GitHub 地址:(欢迎 star 、欢迎推荐 : )
《前端之路》 - TypeScript(三)ES5 中实现继承、类以及原理
《前端之路》- TypeScript (三) ES5 中实现继承、类以及原理
原文:https://www.cnblogs.com/erbingbing/p/12548107.html