继承
原型的继承,不是改变构造函数的原型
function User() {
this.name = function() {
console.log('user name methods');
}
}
User.prototype.show = function(){
console.log('show your name');
}
let ds = new User()
console.log(ds);
ds.show()
function Admin(){}
Admin.prototype = User.prototype
/*
设置Admin独有的,然而因为改变了Admin的原型,会导致这个内容保存在User的原型上面
*/
Admin.prototype.role = function() {
console.log('获取admin---role');
}
let admin = new Admin()
admin.show()
admin.role()
function Member(){}
Member.prototype = User.prototype
let mem = new Member()
mem.role()
原型的继承
改变原型对象的原型
let f = {}
console.log(f.__proto__);
console.log('f', Object.getPrototypeOf(f));
/*
原型的继承,不是改变构造函数的原型
*/
function User() {
this.name = function () {
console.log('user name methods');
}
}
User.prototype.show = function () {
console.log('show your name');
}
let ds = new User()
console.log(ds);
ds.show()
function Admin() { }
// Admin.prototype = User.prototype
Admin.prototype.__proto__ = User.prototype
Admin.prototype.role = function () {
console.log('获取admin---role');
}
let admin = new Admin()
admin.show()
admin.role()
function Member() { }
Member.prototype.__proto__ = User.prototype
Member.prototype.role = function() {
console.log('member.role');
}
let mem = new Member()
mem.role()
上面这种方法,下面的代码顺序没有影响
Admin.prototype.role = function () {
console.log('获取admin---role');
}
Admin.prototype = Object.create(User.prototype)
- Object.create()
- 创建一个对象
- 使用第一个参数的对象作为对象的原型
let f = {}
console.log(f.__proto__);
console.log('f', Object.getPrototypeOf(f));
/*
原型的继承,不是改变构造函数的原型
*/
function User() {
this.name = function () {
console.log('user name methods');
}
}
User.prototype.show = function () {
console.log('show your name');
}
let ds = new User()
console.log(ds);
ds.show()
function Admin() { }
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function () {
console.log('获取admin---role');
}
function Member() { }
Member.prototype.__proto__ = User.prototype
Member.prototype.role = function() {
console.log('member.role');
}
let mem = new Member()
mem.role()
let admin = new Admin()
admin.show()
admin.role()
上面这种方法,下面的代码顺序有影响,因为Object.create创建一个新的,如果把Admin.prototype.role写在Object.create之前,这个是创建在之前的原型上面
Admin.prototype.role = function () {
console.log('获取admin---role');
}
Admin.prototype = Object.create(User.prototype)
继承对新增对象的影响
function Admin() { }
let admin = new Admin()
admin.role()
构造函数构造完之后,立刻实例化对象,这个时候还没有改变构造函数的原型。对象在使用构造函数在创建的时候,会立刻指向构造函数的原型对象
function Admin() { }
let admin = new Admin()
admin.role()
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function () {
console.log('获取admin---role');
}
后面改变构造函数的原型,此时构建函数的新对象还是原来的原型,在新原型上面增加的方法就无法使用
继承对constructor的影响
改变了constructor
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function () {
console.log('获取admin---role');
}
console.dir(Admin); // constructor: User
Admin.prototype = Object.create(User.prototype)
Admin.prototype.constructor = Admin
Admin.prototype.role = function () {
console.log('获取admin---role');
}
console.dir(Admin);
禁止constructor被遍历
function User() {
}
User.prototype.show = function () {
console.log('show your name');
}
function Admin() { }
Admin.prototype = Object.create(User.prototype)
Admin.prototype.constructor = Admin
Admin.prototype.role = function () {
console.log('获取admin---role');
}
console.log(Object.getOwnPropertyDescriptors(Admin.prototype));
console.dir(Admin)
let admin = new Admin()
for (const key in admin) {
console.log('key', key);
}
in遍历会将原型链上的也遍历,这个constructor也会被遍历
解决方法:
function User() {
}
User.prototype.show = function () {
console.log('show your name');
}
function Admin() { }
Admin.prototype = Object.create(User.prototype)
// Admin.prototype.constructor = Admin
Object.defineProperty(Admin.prototype, "constructor", {
value: Admin,
enumerable: false
})
Admin.prototype.role = function () {
console.log('获取admin---role');
}
console.log(Object.getOwnPropertyDescriptors(Admin.prototype));
console.dir(Admin)
let admin = new Admin()
for (const key in admin) {
console.log('key', key);
}
方法重写和父级属性访问
function User(){}
User.prototype.show = function(){
console.log('user.name');
}
User.prototype.site = function(){
return 'https://www.baidu.com'
}
function Admin(){}
Admin.prototype = Object.create(User.prototype)
Admin.prototype.constructor = Admin
// 重写show并使用父级属性方法
Admin.prototype.show = function(){
console.log(User.prototype.site() + 'admin show');
}
let ds = new Admin()
ds.show() // https://www.baidu.comadmin show
面向对象的多态
function User(){}
User.prototype.show = function(){
console.log(this.description());
}
function Admin(){}
Admin.prototype = Object.create(User.prototype)
Admin.prototype.description = function(){
return '管理员在此'
}
function Member(){}
Member.prototype = Object.create(User.prototype)
Member.prototype.description = function(){
return '会员来罗'
}
function Enterprice(){}
Enterprice.prototype = Object.create(User.prototype)
Enterprice.prototype.description = function(){
return '企业账号'
}
for (const key of [new Admin(), new Member(), new Enterprice()]) {
key.show();
}
使用父类构造函数初始属性
function User(name, age){
this.name = name;
this.age = age;
console.log('this', this);
}
User.prototype.show = function(){
console.log('user show');
console.log('this.name', this.name);
console.log('this.age', this.age);
}
// function Admin(name, age){
// User.call(this, name, age)
// }
function Admin(...args){
User.apply(this, args)
}
Admin.prototype = Object.create(User.prototype)
let ds = new Admin('东苏', 18)
ds.show()
使用原型工厂封装继承
function extend(sub, sup){
sub.prototype = Object.create(sup.prototype)
Object.defineProperty(sub, 'constructor', {
value: sub,
enumerable: false
})
}
function User(name, age){
this.name = name;
this.age = age;
console.log('this', this);
}
User.prototype.show = function(){
console.log('user show');
console.log('this.name', this.name);
console.log('this.age', this.age);
}
// function Admin(name, age){
// User.call(this, name, age)
// }
function Admin(...args){
User.apply(this, args)
}
extend(Admin, User)
// Admin.prototype = Object.create(User.prototype)
let ds = new Admin('东苏', 18)
ds.show()
对象工厂派生对象并实现继承
工厂不断产生对象,需要添加方法就在其添加
function User (name, age){
this.name = name;
this.age = age
}
User.prototype.show = function(){
console.log(this.name, this.age);
}
function admin(name, age) {
// const instance = {}
// instance.__proto__ = User.prototype
const instance = Object.create(User.prototype)
User.call(instance, name, age)
instance.role = function(){
console.log('role');
}
return instance
}
let ds = new admin('东苏', 18)
ds.role()
function member (name, age) {
const instance = Object.create(User.prototype)
User.call(instance, name, age)
return instance
}
let mem = new member('会员', 23)
mem.show()
多继承造成的困扰
多继承一直往上继承
function extend(sub, sup){
sub.prototype = Object.create(sup.prototype)
Object.defineProperty(sub, 'constructor', {
value: sub,
enumerable: false
})
}
function Credit(){}
Credit.prototype.total = function(){
console.log('积分统计');
}
function Request(){}
extend(Request, Credit)
Request.prototype.ajax = function(){
console.log('请求后台');
}
function User(name, age){
this.name = name;
this.age = age;
console.log('this', this);
}
extend(User, Request)
User.prototype.show = function(){
console.log(this.name, this.age);
}
function Admin(name, age){
User.call(this, name, age)
}
extend(Admin, User)
// Admin.prototype = Object.create(User.prototype)
let ds = new Admin('东苏', 18)
ds.show()
ds.ajax()
ds.total()
使用mixin实现多继承
// mixin混合功能
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype)
Object.defineProperty(sub, 'constructor', {
value: sub,
enumerable: false
})
}
const Address = {
getAddress() {
console.log('收获地址');
}
}
const Credit = {
total() {
console.log('积分统计');
}
}
const Request = {
ajax() {
console.log('请求后台');
}
}
function User(name, age) {
this.name = name;
this.age = age;
console.log('this', this);
}
User.prototype.show = function () {
console.log(this.name, this.age);
}
function Admin(name, age) {
User.call(this, name, age)
}
extend(Admin, User)
Admin.prototype = Object.assign(Admin.prototype, Credit, Request)
// Admin.prototype = Object.create(User.prototype)
let ds = new Admin('东苏', 18)
ds.show()
ds.ajax()
ds.total()
function Member(name, age){}
extend(Member, User)
Member.prototype = Object.assign(Member.prototype, Address)
let mem = new Member('成员', 16)
mem.getAddress()
使用mixin的内部继承与super关键字
super
当前的原型
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype)
Object.defineProperty(sub, 'constructor', {
value: sub,
enumerable: false
})
}
const Address = {
getAddress() {
console.log('收获地址');
}
}
const Request = {
ajax() {
console.log( '请求后台');
return '请求后台'
}
}
const Credit = {
__proto__: Request,
total() {
// console.log(this.__proto__.ajax()+'积分统计');
// super = this.__proto__
console.log(super.ajax()+'积分统计');
}
}
function User(name, age) {
this.name = name;
this.age = age;
console.log('this', this);
}
User.prototype.show = function () {
console.log(this.name, this.age);
}
function Admin(name, age) {
User.call(this, name, age)
}
extend(Admin, User)
Admin.prototype = Object.assign(Admin.prototype, Credit, Request)
// Admin.prototype = Object.create(User.prototype)
let ds = new Admin('东苏', 18)
ds.show()
ds.ajax()
ds.total()
function Member(name, age){}
extend(Member, User)
Member.prototype = Object.assign(Member.prototype, Address)
let mem = new Member('成员', 16)
mem.getAddress()
类
声明类
class User{}
console.log(typeof User); // function
let ds = class {}
console.log(ds);
class User {
show() { }
get() {
console.log('东苏来罗');
}
}
let ds = new User()
ds.get()
function Article(title) {
this.title = title
}
let a = new Article('小东东')
console.log(a.title);
class User {
constructor(name) {
this.name = name
}
show(){
console.log(this.name);
}
}
let user = new User('susu')
console.log(user.name);
user.show()
类的内部工作机制就是原型操作
class User{}
console.log(typeof User); // function
console.log(User === User.prototype.constructor); // true
console.dir(User)
function Ds(){}
console.log(Ds === Ds.prototype.constructor); // true
console.dir(Ds)
class User{
// constructor 为对象做属性初始值
constructor(name){
this.name = name
}
// 方法直接放在prototype上
show(){}
}
console.log(typeof User); // function
console.dir(User)
let U = new User('小东');
console.log(Object.getOwnPropertyNames(U));
console.log(Object.getOwnPropertyNames(User.prototype));
console.log('----------------------');
function Ds(name){
this.name = name;
}
Ds.prototype.show = function(){}
console.dir(Ds)
let D = new Ds('小酥')
console.log(Object.getOwnPropertyNames(D));
console.log(Object.getOwnPropertyNames(Ds.prototype));
对象属性的声明
class User{
site = '类'
constructor(name){
this.name = name
}
changeSite (value) {
this.site = value
}
show(){
return `${this.site}:${this.name}`
}
}
let ds = new User('东苏')
console.log(ds);
ds.changeSite('https://www.baidu.com')
console.log(ds.show());
let hd = new User('后盾人')
console.log(hd);
console.log(hd.show());
constructor(name){
this.name = name
}
this关键字是对新对象的声明方式,每个对象声明的属性是它独有的,是对象之间不会共享的
class声明的方法为什么不能遍历
function Ds(){}
Ds.prototype.show = function(){}
console.log(JSON.stringify(Object.getOwnPropertyDescriptor(Ds.prototype, 'show'), null, 2));
let D = new Ds()
for (const key in D) {
// in可以遍历原型链上的内容
console.log(key);
}
class User {
constructor(name){
this.name = name
}
show(){}
}
let u = new User('小东')
console.dir(User)
console.log(JSON.stringify(Object.getOwnPropertyDescriptor(User.prototype, 'show'), null, 2));
for (const key in u) {
console.log(key);
}
严格模式下运行
普通的函数
function ds(){
console.log(this); // 非严格模式下为 window
}
ds()
"use strict"
function ds(){
console.log(this); // 严格模式下为 undefined
}
ds()
构造函数
function User(){}
User.prototype.show = function(){
function test(){
console.log(this); // 非严格模式下为 window
}
test()
}
let u = new User()
u.show()
"use strict"
function User(){}
User.prototype.show = function(){
function test(){
console.log(this); // 严格模式下为 undefined
}
test()
}
let u = new User()
u.show()
类
默认就是严格模式
class DS {
show(){
function test(){
console.log('this', this); // undefined
}
test()
}
}
let ds = new DS()
ds.show()
静态属性使用
构造函数
function Web(url){
// 对象的属性
this.url = url
}
// 构造函数的属性:静态属性
Web.url = 'baidu.com'
let ds = new Web('xiaomi.com')
console.log(ds);
console.dir(Web)
类
class Request {
host = "https://www.baidu.com"
}
let obj = new Request()
console.log(obj);
let obj2 = new Request()
obj.host = "https://xiaomi.com"
console.log(obj2);
console.dir(Request)
class Request {
static host = "https://www.baidu.com"
api(url){
return Request.host + `/${url}`
}
}
let obj = new Request()
console.log(obj);
console.log(obj.api('article'));
console.dir(Request)
静态方法的实现原理
通过
构造函数创建的对象自动指向原型
__proto__:把函数当对象的原型
this是指向调用方法的对象
class User {
constructor(name) {
this.name = name
}
static show() {
console.log('static show');
}
}
User.prototype.show = function () {
console.log('prototype show');
}
let user = new User('张三')
console.log(user);
user.show()
User.show()
class User {
constructor(name, age) {
this.name = name
this.age = age
}
static create(...args) {
return new User(...args)
}
}
let user = User.create('zhangsan',18)
console.log(user.name);
console.log(user.age);
class User {
constructor(name, age) {
this.name = name
this.age = age
}
static create(...args) {
return new this(...args)
}
}
let user = User.create('zhangsan',18)
console.log(user.name);
console.log(user.age);
静态属性练习之课程管理类
用普通的方式进行对每一条数据进行处理,当内容过多,这种方法比较冗余麻烦
const data = [
{
name: 'js',
price: 100
},
{
name: 'css',
price: 80
},
{
name: 'vue',
price: 200
}
]
class Lesson {
constructor(data){
this.model = data
}
}
let obj1 = new Lesson(data[0])
console.log(obj1);
let obj2 = new Lesson(data[1])
console.log(obj2);
静态属性
const data = [
{
name: 'js',
price: 100
},
{
name: 'css',
price: 80
},
{
name: 'vue',
price: 200
}
]
class Lesson {
constructor(data){
this.model = data
}
get name() {
return this.model.name
}
get price(){
return this.model.price;
}
static createBatch(data) {
return data.map(item => new this(item))
}
static maxPrice(data) {
return data.sort((a, b) => a.price - b.price)[0]
}
static totalPrice(data) {
return data.reduce((pre, cur) => {
return pre + cur.price
}, 0)
}
}
let lessons = Lesson.createBatch(data)
console.log(Lesson.maxPrice(lessons).price);
console.log(Lesson.totalPrice(lessons));
console.log(lessons);
在类中使用访问器
class Request{
constructor(host){
this.data = {}
this.host = host
}
set host(url) {
if (!/^https?:\/\//i.test(url)){
throw new Error("地址错误")
}
this.data.host = url
/**
不能直接this.host去赋值,因为这样会不断的触发set
*/
}
get host() {
return this.data['host']
}
}
let ds = new Request('https://baidu.com')
// ds.host = '123'
console.log(ds.host);
使用命名规则保护属性
使用下划线特殊符号去命名
class User {
_url = 'https://www.houdunren.com'
constructor(name) {
this.name = name
}
set url(value) {
if (!/^https?:/i.test(value)){
throw new Error('错误地址')
}
this._url = url
}
get url(){
return this._url
}
}
let user = new User('东苏')
console.log(user.name);
// user.url = '123'
上面的例子当使用user._url去做修改的时候,还是可以对其进行修改
公共属性是类的内部外部都可以访问和修改,普通情况下的都是public
使用Symbol定义protected属性
const HOST = Symbol('主机') // 可以直接修改HOST,但是要注意,编程是模块化的,所以上面的代码会放在一个模块里面,只会把类给外面导出数据,HOST不导出,外部是不可见的
class User {
[HOST] = 'https://www.houdunren.com'
constructor(name) {
this.name = name
}
set url(value) {
if (!/^https?:/i.test(value)){
throw new Error('错误地址')
}
this[HOST] = url
}
get url(){
return this[HOST]
}
}
let user = new User('东苏')
console.log(user.name);
// user.url = '123'
// user[Symbol] = 'https://www.baidu.com'
console.log(user.url);
受保护的:在类及其子类中可以被引用
const protecteds = Symbol('主机')
class Common {
// [HOST] = 'https://www.houdunren.com'
constructor(){
this[protecteds] = {}
this[protecteds].host = 'https://www.houdunren.com'
}
set url(value) {
if (!/^https?:/i.test(value)){
throw new Error('错误地址')
}
this[protecteds].host = url
}
get url(){
return this[protecteds].host
}
}
class User extends Common{
constructor(name) {
// 子类调用父类
super()
this[protecteds].name = name
}
get name(){
return this[protecteds].name
}
}
let user = new User('东苏')
console.log(user.name);
// user.url = '123'
console.log(user.url);
使用WeakMap保护属性
定义私有变量,私有变量虽然可以直接修改,但是要注意,编程是模块化的,私有变量会放在一个模块里面,只会把类给外面导出数据,私有变量不导出,外部是不可见的。具体意思请看下面的例子
const host = new WeakMap()
class User {
constructor(name) {
this.name = name;
host.set(this, "https://houdunren.com")
}
set host(url) {
if(!/^https?:/i.test(url)) {
throw new Error('错误地址')
}
host.set(this, url)
}
get host(){
return host.get(this)
}
}
let user = new User('张三')
// user.host = '1233'
host.set(user, "abs") // 这样可以直接修改,但是要注意,编程是模块化的,所以上面的代码会放在一个模块里面,只会把类给外面导出数据,host不导出,外部是不可见的
console.log(user.name);
console.log(user.host);
单个属性
const host = new WeakMap()
class Common {
constructor() {
host.set(this, "https://houdunren.com")
}
set host(url) {
if (!/^https?:/i.test(url)) {
throw new Error('错误地址')
}
host.set(this, url)
}
get host() {
return host.get(this)
}
}
class User extends Common {
constructor(name) {
super()
this.name = name;
}
}
let user = new User('张三')
// user.host = '1233'
// host.set(user, "abs") // 这样可以直接修改,但是要注意,编程是模块化的,所以上面的代码会放在一个模块里面,只会把类给外面导出数据,host不导出,外部是不可见的
console.log(user.name);
console.log(user.host);
let da = new User('东苏')
da.host = "https://www.baidu.com"
console.log(da.host);
多个属性
const protecteds = new WeakMap()
class Common {
constructor() {
protecteds.set(this,{
host: "https://houdunren.com"
})
}
set host(url) {
if (!/^https?:/i.test(url)) {
throw new Error('错误地址')
}
host.set(this, {...protecteds.get(this), url})
}
get host() {
return host.get(this)['host']
}
}
class User extends Common {
constructor(name) {
super()
this.name = name;
}
set name (name) {
protecteds.set(this, { ...protecteds.get(this), name})
}
get name() {
return protecteds.get(this)['name']
}
}
let user = new User('张三')
console.log(user);
console.log(user.name);
console.log(protecteds);
private私有属性使用
private: 只有自己本身可以访问和修改
属性前面加#
class User {
#host = 'https://www.baidu.com'
constructor(name){
this.check(name)
this.name = name
}
set host(url) {
if(!/^https?:/i.test(url)){
console.log('错误网址')
}
this.#host = url
}
/**
私有方法不可以
改成私有属性的结构
*/
// #check(){
#check = () => {
if(this.name.length < 5) {
throw new Error('名字长度不小于五位')
}
return true
}
}
let ds = new User('小东')
ds.host = 'https://www.taobao.com'
// ds.check()
ds.#check() // 私有方法,不允许外部访问
class属性继承原理
class语法糖结构,内部实现本质就是原型、原型链
// function User(name) {
// this.name = name
// }
// function Admin(name) {
// User.call(this, name)
// }
// Admin.prototype = Object.create(User.prototype)
// Admin.prototype.show = function(){}
// console.dir(Admin)
// let ds = new Admin('螺蛳粉')
// console.log(ds);
class User {
constructor(name) {
this.name = name
}
}
class Admin extends User {
constructor(name) {
super(name)
}
}
let ds = new Admin('柳州螺蛳粉')
console.log(ds);
类的方法继承原理
内部实现本质是原型的继承
class User {
show(){
console.log('类的方法');
}
}
class Admin extends User {
constructor(name){
super();
this.name = name
}
}
console.dir(Admin)
let ds = new Admin('东酥')
ds.show()
方法在原型链上(所有对象共享),普通属性属于新实例化出来的对象上
super原理分析
// 模拟
let hd = {
name: 'hd-name',
show() {
console.log('this', this);
console.log('hd-show', this.name);
}
}
let xj = {
name: 'xj-name',
__proto__: hd,
show(){
// this.__proto__.show() // 这个this指向hd
this.__proto__.show.call(this)
console.log('this.__proto_', this.__proto__);
console.log('xj.show');
}
}
xj.show()
class User {
constructor(name){
this.name = name
}
show(){
console.log('user show');
}
}
class Admin extends User {
constructor(name){
super()
this.name = name
}
show(){
super.show()
console.log('admin show');
}
}
console.dir(Admin)
let ds = new Admin('dongsu')
console.log(ds);
ds.show()
多重继承中super的魅力
上面模拟的代码,在多重继承中就无法实现
let common = {
show(){
console.log('common-show');
}
}
let hd = {
__proto__: common,
name: 'hd-name',
show() {
console.log('this', this);
console.log('hd-show', this.name);
// this.__proto__.show.call(this) // 死循环
}
}
let xj = {
name: 'xj-name',
__proto__: hd,
show(){
// this.__proto__.show() // 这个this指向hd
this.__proto__.show.call(this)
// console.log('this.__proto_', this.__proto__);
console.log('xj.show');
}
}
xj.show()
在多重继承中使用super
let common = {
show(){
console.log('common-show');
}
}
let hd = {
__proto__: common,
name: 'hd-name',
show() {
super.show()
console.log('this', this);
console.log('hd-show', this.name);
// this.__proto__.show.call(this) // 死循环
}
}
let xj = {
name: 'xj-name',
__proto__: hd,
show(){
// this.__proto__.show() // 这个this指向hd
// this.__proto__.show.call(this)
// console.log('this.__proto_', this.__proto__);
super.show()
console.log('xj.show');
}
}
xj.show()
constructor中执行super
function User(name, age){
this.name = name
this.age = age
}
function Admin(...args){
User.call(this, ...args)
}
Admin.prototype = Object.create(User.prototype)
let ds = new Admin('东酥', 123)
console.log(ds);
class User{
constructor(name) {
this.name = name;
}
}
class Admin extends User{
constructor(){
super() // 必须写,并且写在子类的this之前
this.name = 'sb'
}
}
let ds = new Admin('冬冬')
console.log(ds);
super()在constructor必须写,并且要在子类自定义属性之前。因为如果父类中也有相同名称的属性,后者会覆盖前者,为了避免这种现象,系统直接禁止这种写法
使用super访问父类方法
class Controller {
sum() {
console.log('parent methods');
return this.data.reduce((pre, cur) => {
return pre + cur.price
}, 0)
}
}
class Lesson extends Controller {
constructor(data) {
super() // 未写方法(例如super.sum()),会调用父类的构造函数constructor
this.data = data
}
info() {
return {
totalPrice: super.sum(),
data: this.data
}
}
}
console.dir(Lesson)
let data = [
{ name: 'js', price: 100 },
{ name: 'mysql', price: 212 },
{ name: 'vue.js', price: 98 }
]
let hd = new Lesson(data)
hd.sum()
console.log(hd.info());
方法的重写
class Common {
sum() {
return this.data.reduce((t, c) => t + c.price, 0)
}
getByKey(key) {
return this.data.filter(item => item.name.includes(key))
}
}
class Controller extends Common { }
class Lesson extends Controller {
constructor(data) {
super() // 未写方法,会调用父类的构造函数constructor
this.data = data
}
info() {
return {
totalPrice: super.sum(),
data: this.data
}
}
getByKey(key) {
return super.getByKey(key).map(item => item.name)
}
}
console.dir(Lesson)
let data = [
{ name: 'js', price: 100 },
{ name: 'mysql', price: 212 },
{ name: 'vue.js', price: 98 }
]
let hd = new Lesson(data)
hd.sum()
console.log(hd.info());
console.log(hd.getByKey('js'));
静态继承原理
这个prototype是作为
构造函数生成实例对象时的,__proto__是本身作为对象的时候使用的
function User(){}
User.site = '后盾人'
User.show = function() {
console.log('User.static.methods');
}
console.dir(User)
function Admin(){}
Admin.__proto__ = User;
console.dir(Admin)
Admin.show()
console.log(Admin.site);
class User {
static site = 'houdunren.com'
static show() {
console.log('User.static.methods');
}
}
class Admin extends User {
}
console.dir(Admin)
Admin.show()
console.log(Admin.site);
有对象数据就不能用静态方法
内置类继承的原型实现
function Arr(...args) {
args.forEach(item => this.push(item))
this.first = function () {
return this[0]
}
this.max = function () {
return this.sort((a, b) => b - a)[0]
}
}
Arr.prototype = Object.create(Array.prototype)
let hd = new Arr(99, 1, 3, 4, 190)
console.log(hd);
使用继承增强内置类
class Arr extends Array {
constructor(...args) {
super(...args)
}
first() {
return this[0]
}
max() {
return this.sort((a, b) => b - a)[0]
}
add(item) {
this.push(item)
}
remove(value) {
let pos = this.findIndex(item => item === value)
this.splice(pos, 1)
}
}
let hd = new Arr(99, 1, 3, 4, 190)
hd.add('好好学习')
console.log(hd);
hd.remove('好好学习')
console.log(hd);
console.log(hd.first());
console.log(hd.max());
mixin混合模式使用技巧
// mixin
let Tool = {
max(key){
console.log('this', this);
return this.data.sort((a, b)=> b[key] - a[key])[0]
}
}
let Arr = {
count(key) {
return this.data.reduce((pre, cur) => pre + cur.price,0)
}
}
class Lesson {
constructor(lessons) {
this.lessons = lessons
}
get data() {
return this.lessons
}
}
const data = [
{ name: 'js', price: 100 },
{ name: 'mysql', price: 212 },
{ name: 'vue.js', price: 98 }
]
Object.assign(Lesson.prototype, Tool, Arr)
console.dir(Lesson);
let hd = new Lesson(data)
console.log(hd.max('price'));
console.log(hd.count('price'));
灵活的动画处理类
<style>
.s1 {
width: 300px;
height: 300px;
background-color: aquamarine;
}
</style>
<div class="s1"></div>
class Animation {
constructor(el) {
this.el = el
this.timeout = 5
this.isShow = true
this.defaultHeight = this.height
}
hide(cb) {
this.isShow = false
let id = setInterval(() => {
console.log('3');
if (this.height <= 0) {
clearInterval(id)
cb && cb()
return
}
this.height = this.height - 1
}, this.timeout);
}
show(cb) {
this.isShow = false
let id = setInterval(() => {
console.log('3');
if (this.height >= this.defaultHeight) {
clearInterval(id)
cb && cb()
return
}
this.height = this.height + 1
}, this.timeout);
}
get height() {
return window.getComputedStyle(this.el).height.slice(0, -2) * 1
}
set height(height) {
this.el.style.height = height + 'px'
}
}
// let el = document.querySelector('.s1')
// let hd = new Animation(el)
// hd.hide(() => {
// console.log('hide完成');
// hd.show(() => {
// console.log('show完成');
// })
// })
class Slide {
constructor(el) {
this.el = document.querySelector(el)
this.links = this.el.querySelectorAll('dt')
this.panels = [...this.el.querySelectorAll('dd')].map(item => new Panel(item))
// this。panels就是Panel对象,对象的好处就是可以使用方法
console.log(this.panels);
// 绑定事件
this.bind()
}
bind() {
this.links.forEach((item, i) => {
console.log(i);
// 批量增加事件
item.addEventListener('click', () => {
console.log('123', i);
this.action(i)
})
});
}
action(i) {
console.log();
// 点击的展示,其余的隐藏
Panel.hideAll(Panel.filter(this.panels, i), () => {
this.panels[i].show()
})
}
}
// 批量控制显示隐藏
class Panel extends Animation {
static num = 0;
// 隐藏多个面板
static hideAll(items, cb) {
items.forEach(item => {
// Panel.num > 0 证明有动画在执行,就不做动画处理
if (Panel.num > 0) return;
Panel.num++
item.hide(() => {
Panel.num--
})
cb && cb()
})
}
// 过滤掉展示的
static filter(items, i) {
return items.filter((item, index) => index != i)
}
}
let hd = new Slide('.s1')
模块
模块管理
let module = (function () {
//模块列表集合
const moduleLists = {};
function define(name, modules, action) {
modules.map((m, i) => {
modules[i] = moduleLists[m];
});
//执行并保存模块
moduleLists[name] = action.apply(null, modules);
}
return { define };
})();
//声明模块不依赖其它模块
/*
参数一:模块名字
参数二:依赖的模块
参数三:回调函数
*/
module.define("hd", [], function () {
return {
show() {
console.log("hd module show");
},
max(arr, key) {
return arr.sort((a, b) => b[key] - a[key])[0]
}
};
});
module.define('lesson', ['hd'], function (hd) {
let data = [
{ name: 'js', price: 99 },
{ name: 'mysql', price: 88 }
]
console.log(hd.max(data, 'price'))
})
//声明模块时依赖其它模块
module.define("xj", ["hd"], function (hd) {
hd.show();
});
模块延迟解析与严格模式
JS从上至下执行
<body>
<script>
console.log(document.querySelector('button')); // null
</script>
<button>button</button>
</body>
模块后解析
<body>
<script type="module">
console.log(document.querySelector('button'));
</script>
<button>button</button>
</body>
========>
<script type="module">
import {} from './hd.js'
</script>
<button>button</button>
// hd.js
console.log(document.querySelector('button'));
模块默认运行在
严格模式
<script type="module">
import {} from './hd.js'
</script>
<button>button</button>
// hd.js
site = 'baidu'
<script type="module">
console.log('模块', this); // undefined
import {} from './hd.js'
</script>
<button>button</button>
<script>
console.log('非模块', this); // window
</script>
作用域在模块中的体现
模块有自己的独立作用域
<script type="module">
// 模块
let site = 'baidu'
</script>
<script>
// 顶级作用域
let url = 'baidu'
</script>
<script>
console.log(url);
console.log(site);
</script>
预解析
模块在导入时只执行一次解析,之后的导入不会再执行模块代码,而使用第一次解析结果,并共享数据。
- 可以在首次导入时完成一些初始化工作
- 如果模块内有后台请求,也只执行一次即可
模块的具名导出与导入
ES6使用基于文件的模块,即一个文件一个模块。
- 使用
export将开发的接口导出 - 使用
import导入模块接口 - 使用
*可以导入全部模块接口 - 导出是以引用方式导出,无论是标量还是对象,即模块内部变量发生变化将影响已经导入的变量
有关于模块打包知识请在 后盾人搜索
webpack
导出
export let site = 'www'
export function show() {
return 'show fun'
}
export class User {
static render() {
return 'user static'
}
}
let site = 'www'
function show() {
return 'show fun'
}
class User {
static render() {
return 'user static'
}
}
export {
site,
show,
User
}
导入
模块默认是在顶层静态导入,这是为了分析使用的模块方便打包
<script type="module">
import {
site,
show,
User
} from './hd4.js'
console.log(site, show(), User.render());
</script>
批量导入与建议
如果要导入的内容比较多,可以使用
*来批量导入。
<script type="module">
import * as api from './hd4.js'
console.log(api);
console.log(api.site);
console.log(api.show());
console.log(api.User.render());
</script>
建议
因为以下几点,我们更建议使用明确导入方式
- 使用
webpack构建工具时,没有导入的功能会删除节省文件大小 - 可以更清晰知道都使用了其他模块的哪些功能
导入导出别名
可以为导入的模块重新命名,下面是为了测试定义的 hd4.js 模块内容。
- 有些导出的模块命名过长,起别名可以理简洁
- 本模块与导入模块重名时,可以通过起别名防止错误
<script type="module">
import { site as s, show1 } from './hd4.js'
let site = '我更改了'
console.log(site);
console.log(s);
console.log(show1());
</script>
let site = 'www'
function show() {
return 'show fun'
}
class User {
static render() {
return 'user static'
}
}
export {
site,
show as show1,
User
}
默认导出
默认导出只能有
一个,默认导出的功能可以使用任意变量接收
export default class User {
static render () {
return 'static render User'
}
}
class User {
static render () {
return 'static render User'
}
}
export { User as default}
<script type="module">
import a from './default.js'
console.log(a.render());
</script>
混合导入导出的使用
export let site = 'www'
export default class User {
static render () {
return 'static render User'
}
}
let site = 'www'
class User {
static render () {
return 'static render User'
}
}
export { User as default, site}
<script type="module">
import de, { site } from './default.js'
console.log(de.render());
console.log(site);
</script>
批量导入混合
<script type="module">
import * as api from './default.js'
console.log(api.default.render());
console.log(api.site);
</script>
对于默认导出和命名导出有以下建议
- 不建议使用默认导出,会让开发者导入时随意命名
- 如果使用默认导入最好以模块的文件名有关联,会使用代码更易阅读
模块合并导出
// default.js
let site = 'www-default'
class User {
static render () {
return 'static render User'
}
}
export { User as default, site}
// hd4.js
let site = 'www'
function show() {
return 'show fun'
}
class User {
static render() {
return 'user static'
}
}
export {
site,
show as show1,
User
}
// m13.js
import * as def from './default.js'
import * as hd4 from './hd4.js'
export {
def,
hd4
}
以组的形式导入导出,避免相同命名的冲突
// conbine.js
import * as api from './m13.js'
console.log(api);
console.log(api.def.default.render());
console.log(api.hd4.site);
console.log(api.def.site);
<script type="module" src="./conbine.js"></script>
按需动态加载模块
使用 import 必须在顶层静态导入模块,而使用import() 函数可以动态导入模块,它返回一个 promise 对象。
<button>按钮</button>
<script type="module">
// import { site } from './default.js'
// console.log(site);
document.querySelector('button').addEventListener('click', function () {
import('./default.js').then(({ site, User }) => {
console.log(site, User);
})
})
</script>
指令总结
| 表达式 | 说明 |
|---|---|
| export function show(){} | 导出函数 |
| export const name='后盾人' | 导出变量 |
| export class User{} | 导出类 |
| export default show | 默认导出 |
| const name = '后盾人' export {name} | 导出已经存在变量 |
| export {name as hd_name} | 别名导出 |
| import defaultVar from 'houdunren.js' | 导入默认导出 |
| import {name,show} from 'a.j' | 导入命名导出 |
| Import {name as hdName,show} from 'houdunren.js' | 别名导入 |
| Import * as api from 'houdunren.js' | 导入全部接口 |