1. 认识类
64.PNG// 类的声明
class Person {
}
// babel
// 类的表达式(用的比较少)
// var Animal = class {
// }
// 研究一下类的特性
console.log(Person.prototype)
console.log(Person.prototype.__proto__)
console.log(Person.prototype.constructor)
console.log(typeof Person) // function
var p = new Person()
console.log(p.__proto__ === Person.prototype) // true
//总结
/*
class Person{
}
就是 function Person(){
}
构造函数的语法糖。
2. class的构造方法
// 类的声明
class Person {
// 类的构造方法
// 注意: 一个类只能有一个构造函数,这个构造函数在new的时候就会被调用,然后按照以下步骤进行运行
// 1.在内存中创建一个对象 moni = {}
// 2.将类的原型prototype赋值给创建出来的对象 moni.__proto__ = Person.prototype
// 3.将对象赋值给函数的this: new绑定 this = moni
// 4.执行函数体(构造函数constructor)中的代码
// 5.如果构造函数没有返回非空对象,则返回创建出来的新对象;
constructor(name, age) {
this.name = name
this.age = age
}
}
var p1 = new Person("why", 18)
var p2 = new Person("kobe", 30)
console.log(p1, p2)
3. JavaScript 对象访问器(补充)
ECMAScript 5 (2009) 引入了 Getter 和 Setter。
JavaScript Getter(get 关键词)
//本例使用 lang 属性来获取 language 属性的值。
// 创建对象:
var person = {
firstName: "Bill",
lastName : "Gates",
language : "en",
get lang() {
return this.language;
},
set lang(newLang) {
console.log('setting lang');
this.language = newLang;
}
};
console.log(person.lang);//en
//意味着不对langeuage直接进行操作,而是通过lange这个方法进行对language的操作
person.lang = "CN";
console.log(person.lang);//CN
优点1:提供更简洁的语法
下面两个例子的区别在哪里?
例子 1
var person = {
firstName: "Bill",
lastName : "Gates",
fullName : function() {
return this.firstName + " " + this.lastName;
}
};
// 使用方法来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.fullName();
例子 2
var person = {
firstName: "Bill",
lastName : "Gates",
get fullName() {
return this.firstName + " " + this.lastName;
}
};
// 使用 getter 来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.fullName;
例子 1 以函数形式访问 fullName:person.fullName()。
例子 2 以属性形式访问 fullName:person.fullName。
第二个例子提供了更简洁的语法。
优点2: 提高数据质量
使用 getter 和 setter 时,JavaScript 可以确保更好的数据质量。
在本例中,使用 lang 属性以大写形式返回 language 属性的值:
// Create an object:
var person = {
firstName: "Bill",
lastName : "Gates",
language : "en",
get lang() {
return this.language.toUpperCase();
}
};
// 使用 getter 来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.lang;
4.class中的方法定义
var names = ["abc", "cba", "nba"];
class Person{
constructor(name, age){
this.name = name;
this.age = age;
this._address = "广州市";
}
//这里的eating和running,还有get和set本质上就是放在Person的prototype上的
//console.log(Object.getOwnPropertyDescriptors(Person.prototype))
eating(){
console.log(this.name + "eating");
}
runnning(){
console.log(this.name + "running");
}
//类的访问器方法
get address(){
console.log("访问器拦截操作");
return this._address
}
set address(newAddress){
console.log("拦截设置操作");
this._address = newAddress;
}
//以上eating和runnning是通过创建实例对象,让实例对象进行访问,比如var p = new Person() p.eating()
//访问器方法也是通过实例对象去访问的
//类的静态方法(类方法) ,不需要去创建实例对象,通过类名直接去访问
// Person.createPerson()
//静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义:
//静态方法不会被实例继承
static randomPerson(){
var nameIndex = Math.floor(Math.random()* names.length);
var name = names[nameIndex];
var age = Math.floor(Math.random()*100);
return new Person(name, age)
}
}
var p = new Person("why", 18);
p.eating();
p.runnning();
console.log(p.address)
p.address = "北京市"
console.log(p.address)//北京市
var p1 = Person.randomPerson();//我们想创建出来一个对象,但是名字和年龄都是随机出来的
console.log(p1);
for (var i = 0; i < 50; i++) {
console.log(Person.randomPerson())//创建出来50个person对象
}
5 class中实现继承extends
1.1.父类属性的继承(super)
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
// Student称之为子类(派生类)
/* class Student extends Person {
constructor(name, age, sno) {
this.name = name;
this.age = age;
this.sno = sno
}
} */
//会报错
//test.html:60 Uncaught ReferenceError: Must call super constructor in derived class before accessing
// 'this' or returning from derived constructor
//子类必须调用父类的构造方法
// JS引擎在解析子类的时候就有要求, 如果我们有实现继承
//那么子类在子类的构造方法中, 在使用this之前,需要调用父类的构造方法
//使用super关键字
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
}
var stu = new Student("why", 18, 111);
console.log(stu);
//Student {name: 'why', age: 18, sno: 111}
如果子类没有自己的构造函数
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {}
var stu = new Student("why", 18);
console.log(stu);
//Student {name: 'why', age: 18}
//如果子类没有定义constructor()方法,这个方法会默认添加,
//并且里面会调用super()。也就是说,不管有没有显式定义,任何一个子类都有constructor()方法。
class Student extends Person {}
// 等同于
class Student extends Person {
constructor(...args) {
super(...args);
}
}
67.PNG
2. 父类原型方法的继承
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
studying() {
console.log(this.name + " studying~")
}
}
var stu = new Student("why", 18, 111);
console.log(stu);
//Student {name: 'why', age: 18, sno: 111}
stu.studying();
stu.running();
stu.eating();
//在这里,studing是在Student的原型对象上的,running和eating是在Person的原型对象上的。
console.log(Object.getOwnPropertyDescriptors(stu.__proto__))//{constructor: {…}, studying: {…}}
console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__))//{constructor: {…}, running: {…}, eating: {…}}
//Student的原型对象的__proto—__指向Person的原型对象
console.log(Student.prototype.__proto__ === Person.prototype);//ture
console.log(stu.__proto__.__proto__ === Person.prototype);//ture
3. 子类对父类原型方法的重写
情况1:重写
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
studying() {
console.log(this.name + " studying~")
}
// 类对父类的方法的重写
running() {
console.log("student " + this.name + " running")
}
}
var stu = new Student("why", 18, 111);
console.log(stu);
//如果子类不满意父类的running方法,可以在自己里面重新实现running方法
//到时候就会优先找自己的,而不是父类里面的
stu.running();//student why running
情况2:基于父类的逻辑重写
比如父类有下面一个方法
personMethod() {
console.log("处理逻辑1")
console.log("处理逻辑2")
console.log("处理逻辑3")
}
但是子类基于这个方法还有更多的逻辑
personMethod() {
console.log("处理逻辑1")
console.log("处理逻辑2")
console.log("处理逻辑3")
console.log("处理逻辑4")
console.log("处理逻辑5")
console.log("处理逻辑6")
}
这时候完全像上面一样重写的话,就会出现重复代码,没有必要
这时候就需要使用super的第二个用法:
在子类的实例方法里面调用父类的方法
// 重写personMethod方法
personMethod() {
// 复用父类中的处理逻辑
super.personMethod()
console.log("处理逻辑4")
console.log("处理逻辑5")
console.log("处理逻辑6")
}
整体代码:
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
personMethod() {
console.log("处理逻辑1")
console.log("处理逻辑2")
console.log("处理逻辑3")
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
studying() {
console.log(this.name + " studying~")
}
// 重写personMethod方法
personMethod() {
// 复用父类中的处理逻辑
super.personMethod()
console.log("处理逻辑4")
console.log("处理逻辑5")
console.log("处理逻辑6")
}
}
var stu = new Student("why", 18, 111);
console.log(stu);
stu.personMethod();
/*
处理逻辑1
test.html:64 处理逻辑2
test.html:65 处理逻辑3
test.html:84 处理逻辑4
test.html:85 处理逻辑5
test.html:86 处理逻辑6
*/
4.静态方法的继承
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
static staticMethod() {
console.log("PersonStaticMethod");
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
}
var stu = new Student("why", 18, 111);
//调用Person的静态方法
Student.staticMethod();//PersonStaticMethod
5.子类对父类静态方法的重写
情况1:完全重写
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
static staticMethod() {
console.log("PersonStaticMethod")
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
// 重写静态方法
static staticMethod() {
console.log("StudentStaticMethod")
}
}
var stu = new Student("why", 18, 111);
var p = new Person();
console.log(Person.hasOwnProperty("name")); //true
//调用Person的静态方法
Student.staticMethod();//StudentStaticMethod
情况2:基于父类的重写
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
static staticMethod() {
console.log("PersonStaticMethod")
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
// 重写静态方法
static staticMethod() {
super.staticMethod()
console.log("StudentStaticMethod")
}
}
var stu = new Student("why", 18, 111);
//调用Person的静态方法
Student.staticMethod();
//PersonStaticMethod
//StudentStaticMethod
6. ES6转ES5的代码
Babel · The compiler for next generation JavaScript (babeljs.io)
Babel · The compiler for next generation JavaScript (babeljs.io)
IE:Edge 15可以支持 96% 的 ES6 新特性。Edge 14 可以支持 93% 的 ES6 新特性。(IE7~11 基本不支持 ES6)
es5
class Person{}
es5
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Person = function Person() {
"use strict";
//检查this是不是Person创建出来的,
//是的话,那么就可以用instanceof来判断,instanceof就是判断某个对象是不是另一个对象的实例
_classCallCheck(this, Person);
};
//_classCallCheck的作用就是不让你把Person当成一个普通的函数去调用它
//比如Person() 这种情况下this是window或者undifined
//而是希望new Person()
es6
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
eating(){
console.log(this.name + "eating")
}
}
es5
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
//获取当前对象
var descriptor = props[i];
//console.log(descriptor);//{key: 'eating', value: ƒ}
//console.log(descriptor.enumerable);//undefined
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
//console.log(descriptor);
/* configurable: true
enumerable: false
key: "eating"
value: ƒ eating()
writable: true */
}
}
/*
相当于给Person.prototype里面加入eating方法
*/
function _createClass(Constructor, protoProps, staticProps) {
//如果是构造函数的话,直接给Person.prototype添加属性。
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
//如果是构造函数的话,直接给Person添加方法。
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
//在这里,为什么要使用立即执行函数呢?两个原因
//1.它可以独立创建一个作用域,不会污染到全局,每个立即执行函数内的变量都是局部变量,不会有命名冲突的情况。
//2.方便进行进行tree-shaking处理
//标记这个函数为纯函数/*#__PURE__*/--》没有副作用
//纯函数被webpack做压缩的时候,是要做tree-shaking处理
//到时候对代码进行压缩的时候,发现这个函数没有被用过,在tree-shaking的时候,进行优化,直接删除掉这些代码,打包出来的包size小
var Person = /*#__PURE__*/ (function () {
"use strict";
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [
{
key: "eating",
value: function eating() {
console.log(this.name + "eating");
}
}
]);
return Person;
})();
8.7 es6转es5的继承的代码
es6
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
running(){
console.log(this.name + " runnning");
}
}
class Student extends Person{
constructor(name, age, sno){
super(name, age);
this.sno = sno;
}
studying(){
console.log(this.name + " studying");
}
}
es5
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj &&
typeof Symbol === "function" &&
obj.constructor === Symbol &&
obj !== Symbol.prototype
? "symbol"
: typeof obj;
};
}
return _typeof(obj);
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
/* 这里的目的是让Student.__proto__ = Person
为了实现静态方法的继承
比如Student.staticMethod()
这里staticMathod没有在Student对象里面,
我们就要去Student.__proto__里面去找
Student.__proto__默认指的是Function.prototype,但是Function.prototype里面也没这个方法
我们就通过Student.__proto__ = Person , 这里Person里面有这个方法
*/
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
} else if (call !== void 0) {
throw new TypeError(
"Derived constructors may only return object or undefined"
);
}
return _assertThisInitialized(self);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return self;
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(
Reflect.construct(Boolean, [], function () {})
);
return true;
} catch (e) {
return false;
}
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
var Person = /*#__PURE__*/ (function () {
"use strict";
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [
{
key: "running",
value: function running() {
console.log(this.name + " runnning");
}
}
]);
return Person;
})();
var Student = /*#__PURE__*/ (function (_Person) {
"use strict";
_inherits(Student, _Person);
/*
和以前寄生时候实现的方法是一样的,让Student继承Person
function inheritPrototype(SubType, SuperType) {
SubType.prototype = Object.create(SubType.prototype);
Object.defineProperty(SuperType.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: SubType
})
}
*/
var _super = _createSuper(Student);
function Student(name, age, sno) {
var _this;
//以前直接Person.call(this, name, age, friend)来实现的
//但是Person不能直接调用Person()在es6里面
_classCallCheck(this, Student);
_this = _super.call(this, name, age);
_this.sno = sno;
return _this;
}
_createClass(Student, [
{
key: "studying",
value: function studying() {
console.log(this.name + " studying");
}
}
]);
return Student;
})(Person);
// Super: Person
// arguments: ["why", 18]
// NewTarget: Student
// 会通过Super创建出来一个实例, 但是这个实例的原型constructor指向的是NewTarget
// 会通过Person创建出来一个实例, 但是这个实例的原型constructor指向的Person
//Reflect.construct(Super, arguments, NewTarget);
8.8 继承内置类
我们也可以让我们的类继承自内置类,比如Array
// class Person {
// }
// class Student extends Person {
// }
class HYArray extends Array {
firstItem() {
return this[0]
}
lastItem() {
return this[this.length-1]
}
}
var arr = new HYArray(1, 2, 3)
console.log(arr.firstItem())//1
console.log(arr.lastItem())//3
8.9 类的混入mixin
JavaScript的类只支持单继承:也就是只能有一个父类
- 那么在开发中我们我们需要在一个类中添加更多相似的功能时,应该如何来做呢?
- 这个时候我们可以使用混入(mixin);
class Person {
}
/* class Runner{
running(){
}
}
*/
function mixinRunner(BaseClass) {
class NewClass extends BaseClass {
running() {
console.log("running~")
}
}
return NewClass
}
// 在JS中类只能有一个父类: 单继承
class Student extends Person {
}
var NewStudent = mixinRunner(Student)
var ns = new NewStudent()
ns.running()//running~
68.PNG
混入两个类
class Person {
}
/* class Runner{
running(){
}
}
class Eater{
eatting(){
}
} */
function mixinRunner(BaseClass) {
class NewClass extends BaseClass {
running() {
console.log("running~")
}
}
return NewClass
}
function mixinEater(BaseClass) {
return class extends BaseClass {
eating() {
console.log("eating~")
}
}
}
// 在JS中类只能有一个父类: 单继承
class Student extends Person {
}
var NewStudent = mixinEater(mixinRunner(Student))
var ns = new NewStudent()
ns.running()
ns.eating()
弊端:以上方法没办法混入属性,或者在构造函数内传入一些参数
10 JavaScript中的多态
69.PNG// 传统的面向对象多态是有三个前提:
// 1> 必须有继承(是多态的前提)
// 2> 必须有重写(子类重写父类的方法),如果不重写,调用的都是父类的getArea()(同一种行为),认为是没有多态的体现的。重写的话,传入r的情况,调入的是Rectangle类里面的getArea()方法,
//传入c的情况下,调用的是Circle类里面的getArea()方法,
//这样,就可以认为不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样,
// 3> 必须有父类引用指向子类对象
// Shape形状
class Shape {
getArea() {}
}
class Rectangle extends Shape {
getArea() {
return 100
}
}
class Circle extends Shape {
getArea() {
return 200
}
}
var r = new Rectangle()
var c = new Circle()
// 多态: 当对不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样, 那么就是多态的体现.
function calcArea(shape: Shape) {
console.log(shape.getArea())
}
calcArea(r)
calcArea(c)
export {}
// 多态: 当对不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样, 那么就是多态的体现.
function calcArea(foo) {
console.log(foo.getArea())
}
var obj1 = {
name: "why",
getArea: function() {
return 1000
}
}
class Person {
getArea() {
return 100
}
}
var p = new Person()
calcArea(obj1)
calcArea(p)
// 也是多态的体现
function sum(m, n) {
return m + n
}
sum(20, 30)
sum("abc", "cba")
网友评论