4.①面向对象②7种继承③单线程④同步异步⑤回调函数⑥事件循环宏观微观process.nextTick>promise.then>setTimeout>setImmediate⑦WebWorker

目录

一:继承(原型继承、构造函数继承、组合继承)

1.构造函数继承 

①优点

②缺点:

2.原型链式继承

①优点

②缺点

3.组合继承(构造函数继承属性、原型继承方法 一起使用)

①优点

②缺点

4.原型式继承   (也是Object.create()的手写,本质是一个浅拷贝)

①优点:子类能访问父类原型上的方法

②缺点: 子类之间引用地址值共享

5.寄生式继承

①优点:子类能访问父类原型上的方法(同原型式继承一样)

②缺点: 子类之间引用地址值共享(同原型式继承一样)

6.寄生组合式继承

①优点

7.ES6的extends继承(记得要写super关键字)

优点

二:进程和线程

JS:单线程的异步语言

三:回调函数:函数作为参数传递给另外一个函数。例如定时器、事件、ajax、生命周期回调函数

四:同步异步:

五:事件循环(EventLoop)

1.事件队列(调用栈)执行同步代码(先)

2.任务队列   执行异步代码(后)

①宏观任务 macrotask

②微观任务 microtask

任务优先级:process.nextTick>promise.then>setTimeout>setImmediate(后两个如果延迟时间一致,则out先执行,如果out有延迟时间,immediate没有,则immediate先执行)

macrotask宏任务:包含执行整体的js代码script,定时器(setTimeout、setInterval、 setImmediate),读取文件,IO操作,UI render,事件回调,XHR回调,

microtask微任务:更新应用程序状态的任务,promise.then及回调,MutationObserver,process.nextTick,Object.observe

3.练习

六:Web Worker(模拟多线程) H5提供了js多进程的实现  _____浏览器输出报错。。。了解即可

              * Worker.prototype.postMessage(): 主进程和子线程相互发送消息

              * Worker.prototype.onmessage = function(event){}: 用于接收另一个线程的回调函数;onmessage里面有事件对象event,event上面有一个属性叫做data,用来接收主线程传递过来的消息


一:继承(原型继承、构造函数继承、组合继承)

参考链接

  • 面向对象语言三大特征:封装、继承、多态。对比我们从前学过的java语言,他有类Class,但是我们的js(es5之前)没有,(es5)之前我们可以将构造函数理解为类,但是在es6当中,出现了新的关键字 class类  但是他也不是真正的类,底层依旧是通过封装构造函数来实现的,其本质还是一个语法糖
  • 继承:核心问题是让 子类函数 能够继承 父类函数已有的属性和方法
关于父类、子类、及属性:

类:Dog                       (父类)

品种:柯基、哈士奇等等         (子类)

属性:名字、颜色、年龄

1.构造函数继承 

父.call(子,形参1,形参2...)

父.apply(子,arguments)         apply第二个参数必须为数组,所以我们可以用arguments

在子类的内部通过call()或者apply()改变父类中this的指向变成子类,并且传递参数

①优点

  • 没有引用值共享问题

②缺点:

  • 子类的prototype不指向父类,无法访问父类prototype上的属性方法
    <script>
        // 父类方法
        function Dog(name,age){
            this.name = name
            this.age = age
            this.x = [1,2,3,4]
            this.eat=function(){
                console.log('我在吃饭')
            }
        }
        // 父类原型链上添加方法
        Dog.prototype.play = function(){
            console.log('我叫:'+this.name+'我喜欢玩游戏')
        }
        // 子类方法
        function keji(name,age,hobby){
            Dog.call(this,name,age)
            // 或者用apply,,后面的形参是一个伪数组arguments
            // Dog.apply(this, arguments);
            this.hobby = hobby
        }
        var father = new Dog('父',10)        //父类实例化对象
        var child = new keji('子',2,'eat')   //子类实例化对象
        console.log(father)
        console.log(child)
        // child实例不是父类Dog的实例,只是子类keji的实例(所以不能访问Dog.prototype上定义的方法)
        console.log(father instanceof Dog)    // true
        console.log(child instanceof Dog)     // false
        /* 可以方便的继承父类型的属性,但是无法继承原型中的方法(即不能访问Dog.prototype上定义的方法)
        ,因此所有方法属性都写在构造函数中,每次创建实例都会初始化 */
        father.eat()    // 我在吃饭
        child.eat()     // 我在吃饭
        
        // 测试 1. 缺点: 子类不能调用父类原型上的方法 
        father.play()   // 我叫:父我喜欢玩游戏
        child.play()    //  TypeError: child.play is not a function

        // 测试 2. 优点: 子类们没有引用值共享问题  
        var child2 = new keji('子',2,'eat')   //子类实例化对象
        child.x.push(8)
        console.log(child.x)
        console.log(child2.x)
    </script>

2.原型链式继承

子类的原型等于父类的实例化对象

①优点

  • 子类可以访问父类原型上的方法

②缺点

  • 有引用值共享的问题,一个子类修改引用值,所有子类都跟着修改了
  • 创建子类型的时候,不能向父类型的构造函数中传递参数。
    <script>
        // 父类方法
        function Dog(name){
            this.name = '父类'
            this.x = [1,2,3,4]
        }
        // 父类原型链上添加方法
        Dog.prototype.play = function(){
            console.log('我叫:'+this.name+'我喜欢玩游戏')
        }
        // 子类方法
        function keji(name,age){
           this.name = '子类'
        }

        //  子类的原型等于父类的实例化对象
        keji.prototype = new Dog()
        keji.prototype.constructor = keji;   //手动的将keji原型上的构造器属性重新指向keji
    
        var  father = new Dog()
        var  child1 = new keji()
        // 测试  1: 优点 :能访问父类原型上的方法
        child1.play()    //我叫:子类我喜欢玩游戏
        father.play()    //我叫:父类我喜欢玩游戏
        
        // 测试  2: 缺点 :有地址值共享的问题
        var  child2 = new keji()
        child1.x[0] = 5 
        console.log(child1.x)  // [5, 2, 3, 4]
        console.log(child2.x)  // [5, 2, 3, 4]

        // 测试  3:  修改构造器指向
        // 正常的 实例化对象x的构造器constructor 指向 fn()
        function fn(){
            this.s = 5
        }
        let x = new fn()
        console.log(x)         // 正常的输出指向x
        //继承后的keji构造函数 丢失constructor构造器,我们需要手动将keji原型上的构造器属性重新指向keji
        console.log(child1)    // 输出可以看到  构造器指向指回了 keji
    </script>

3.组合继承(构造函数继承属性、原型继承方法 一起使用)

在子类的内部调用父类,通过call()或者apply()改变父类中this的指向变成子类,并且传递参数

子类的原型等于父类的实例化对象

①优点

  • 可以访问父类原型中的方法

  • 引用类型不共享地址值

②缺点

  • 父类构造函数被重复调用两次(第一次是子类原型指向父类实例是,第二次是子类方法内部用.call的时候),子类原型中会存在两份相同的属性/方法。
    <script>
        // 父类构造函数
        function Dog(name,age){
            this.name = name
            this.age = age
            this.x = [1,2,3,4]
            this.eat = function(){
                console.log('我叫:'+this.name+'我爱吃')
            }
        }
        // 父类的原型中添加方法
        Dog.prototype.play = function(){
            console.log('我叫:'+this.name+'我喜欢玩')
        }

        // 子类构造函数
        function keji(name,age){
            this.name = name
            this.age = age
            Dog.apply(this,arguments)  //测试 3: 缺点 第二次执行
        }

        // 修改子类原型指向父类实例化对象
        keji.prototype = new Dog()     //测试 3: 缺点 第一次执行
        keji.prototype.constructor =  keji;
        let father = new Dog('父类',18)
        let child = new keji('子类',12)
        
        // 测试   1:  优点:子类可以访问父类原型上的方法
        father.play()    //我叫:父类我喜欢玩
        child.play()     //我叫:子类我喜欢玩

        // 测试   2:  优点: 子类地址值不共享
        let child2 = new keji('子类2',16)
        child2.x[0] = 5 
        console.log(child.x)    //[1, 2, 3, 4]
        console.log(child2.x)   //[5, 2, 3, 4]
    </script>

4.原型式继承   (也是Object.create()的手写,本质是一个浅拷贝

父类构造函数里面创建子类的构造函数,将子类的原型指向父类,return 回 子类的实例化对象

①优点:子类能访问父类原型上的方法

②缺点: 子类之间引用地址值共享

    <script>
        // ① 创建一个object方法并且传参一个父类对象,
        function object(Dog){
            // ② object方法里面有创建一个子类的构造函数,
            function keji(){}
            // ③ 将子类构造函数的原型指向被传参的父类对象,
            keji.prototype = Dog  // 子类原型指向父类构造函数
            // ④ return 子类构造函数的实例化对象
            return new keji()
        }
        
        var father = {
            name:'父类',
            x:[1,2,3,4,5],
        }
        // 原型上添加一个方法
        father.__proto__.say = function(){
            console.log('hi')
        }
        // 测试 1: 缺点:子类之间存在引用地址值共享问题
        var child1 = object(father)  //child 是 new keji 的实例化对象
        var child2 = object(father)
        child1.x[0] = 5
        console.log(child1.x)   //[5, 2, 3, 4, 5]
        console.log(child2.x)   //[5, 2, 3, 4, 5]
        console.log(child2.s)   //[5, 2, 3, 4, 5]

        // 测试 2: 优点: 子类能访问父类原型上的方法
        father.say()           //hi
        child1.say()           //hi
        child2.say()           //hi
    </script>

5.寄生式继承

就是给原型式继承外面套个壳子 给实例化对象统一添加属性方法,然后再赋值给子实例化对象们

①优点:子类能访问父类原型上的方法(同原型式继承一样)

②缺点: 子类之间引用地址值共享(同原型式继承一样)

    <script>
        function object(Dog){
            function keji(){}
            keji.prototype = Dog
            return new keji()
        }
        // 给总实例化对象内部添加属性方法
        function out(father){
            var childs = object(father)  // childs 为 keji 的实例化对象
            childs.sayHi = function(){   // 统一给实例化对象上面添加sayHi方法
                console.log('hi')
            }
            return childs               //返回 添加了新属性方法的 childs 实例化对象
        }

        var father = {
            name:'父类',
            x:[1,2,3,4,5],
            eat:function(){
                console.log('我在吃')
            }
        }
        
        // 子实例化对象们
        var child1 = out(father)
        var child2 = out(father)
        // 测试  1: 缺点:有子类引用地址值共享
        child1.x[0] = 5
        console.log(child1.x)  // [5, 2, 3, 4, 5]
        console.log(child2.x)  // [5, 2, 3, 4, 5]

        // 测试  2: 优点:子类能否访问父类原型上的方法
        child1.eat()   //  我在吃
        child2.eat()   //  我在吃
    </script>

6.寄生组合式继承

结合借用构造函数继承(没有引用地址值共享问题)再修改子类原型指向父类实现继承,

①优点

  • 修复了组合继承构造函调用两次的缺点
  • 借用了构造函数继承,在子类构造函数的内部借用.call将父类的this指向修改为子类
  • 修改了子类构造函数的原型→子类可以访问父类原型上的方法
    <script>
        // 父类构造函数的方法
        function Dog(name){
            this.name = name,
            this.x = [1,2,3,4,5]
        }
        // 父类原型上添加方法
        Dog.prototype.sayHi = function(){
            console.log('hi')
        }
        // 子类构造函数  (解决①子类引用地址值共享问题)
        function keji(name){
            this.name = name
            Dog.call(this,name)  //在子类构造函数内部,将父类的this指向修改为子类
        }
        // 将子类构造函数原型指向父类构造函数原型 (解决②子类无法访问父类原型上的方法问题)
        function revisePrototype(keji,Dog){
            // 将子类原型指向父类原型
            Object.setPrototypeOf(keji.prototype,Dog.prototype)
            // 将子类的constructor构造器指回自己
            keji.constructor = keji;
        }
        // 执行
        revisePrototype(keji,Dog)

        // 测试  优点:1.子类可以访问父类原型上的方法
        let child = new keji()
        child.sayHi()
        // 测试  优先:2.子类之间不存在引用地址值共享问题
        let child2 = new keji()
        child.x[0] = 5
        console.log(child.x)     //[5, 2, 3, 4, 5]
        console.log(child2.x)    // [1, 2, 3, 4, 5]
    </script>

7.ES6的extends继承(记得要写super关键字)

关于ES6新增的class类此链接说明

优点

  • 可以访问父类构造函数上的方法
  • 子类实例化对象之间不存在引用地址值共享的问题
    <script>
        class Dog{
            // 公共属性
            constructor(name,age){
                this.name = name
                this.age = age
                this.ceshi = '测试'
            }
            // 公共方法
            eatF() {
                console.log('我是父类:我爱吃')
            }
            // 私有,Dog类有的私有属性和私有方法,实例化对象不继承私有的
            static s = '我是Dog父类私有的属性'
            static play(){
                console.log('我是Dog父类私有的方法')
            }
        }
        // 给父类的原型上添加方法
        Dog.prototype.sayHi = function(){
            console.log('我是父类原型上的方法')
        }
        // 子类 extends 继承
        class keji extends Dog{
            // keji的公共属性
            constructor(name,age){
                // 初始化this,相当于Dog.call(this,name,age)   ,必须先super,才能使用this关键字
                super(name,age)
                this.x = [1,2,3,4,5]    
            }
            // keji的公共方法
            eatZ() {
                console.log('我是子类:我爱吃')
            }
        }

        // 创建子类和父类的实例化对象
        let father = new Dog()
        var child1 = new keji('小杨1',18)

        // 测试 1: 优点 可以访问父类构造函数上的方法
        father.sayHi()         // 我是父类原型上的方法
        child1.sayHi()         // 我是父类原型上的方法

        // 测试 2: 优点 子类实例化对象之间不存在引用地址值共享的问题
        var child2 = new keji('小杨2',100)
        child1.x[0] = 5
        console.log(child1.x)  // [5, 2, 3, 4, 5]
        console.log(child2.x)  // [1, 2, 3, 4, 5]
    </script>

二:进程和线程

1.进程是比如打开QQ,打开浏览器,这是两个进程

2.进程是由多个线程组成的,线程分为单线程和多线程

多线程:浏览器,可以同时运行多个页面

单线程:JS

JS:单线程的异步语言

  • JS是单进程的,只能同时做一件事,遇到等待比如网络不好卡住了怎么办?异步就是解决单线程等待的问题
  • 浏览器和nodejs已支持JS启动进程,如Web Worker
  • JS和DOM渲染共用同一个线程,因为JS可修改DOM解构
  • 异步是基于 callback回调函数 形式进行调用的

三:回调函数:函数作为参数传递给另外一个函数。例如定时器、事件、ajax、生命周期回调函数

你定义的,你没调用,但是最终执行了

四:同步异步:

事件绑定语句是同步,回调函数的触发是异步

定时器的启动是同步,定时器的触发是异步

同步代码会阻塞,前一个执行完才能执行下一个

1.同步:同步执行完成才会去执行异步,会阻塞代码执行

2.异步:只要是异步的任务都会有自己的管理模块进行托管,不会阻塞代码运行(回调函数都是异步)

五:事件循环(EventLoop

解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

同步》微任务》宏任务

1.事件队列(调用栈)执行同步代码(先)

基础console.log语句 和 new 执行的代码是同级的  无优先级

2.任务队列   执行异步代码(后)

①宏观任务 macrotask

②微观任务 microtask

任务优先级:process.nextTick>promise.then>setTimeout>setImmediate(后两个如果延迟时间一致,则out先执行,如果out有延迟时间,immediate没有,则immediate先执行)

macrotask宏任务:包含执行整体的js代码script,定时器(setTimeoutsetInterval、 setImmediate),读取文件,IO操作,UI render,事件回调,XHR回调,

microtask微任务:更新应用程序状态的任务,promise.then及回调,MutationObserverprocess.nextTickObject.observe

3.练习

  • 两个微任务同时存在时,不要一直沿着一个微任务深入思考,应该先让微任务1整体入队,微任务2整体入队,然后再将微任务1中的小微任务在微任务2后入队

注意: .then 执行的条件是:Promise的状态必须改变 也就是 resolve() 

 2,5,6,8,7,3,1,4

1,5,2,4,3

5,1,4,3,6,2

    <script>
        setTimeout(() => {
            console.log('0');
        },0)
        new Promise((resolve,reject) => {
            console.log('1');
            resolve()
        }).then(() => {
            console.log('2');
            new Promise((resolve,reject) => {
                console.log('3');
                resolve()
            }).then(() => {
                console.log('4');
            }).then(() => {
                console.log('5');
            }).then(() => {
                console.log('100');
            })
        }).then(() => {
                console.log('6');
        })

        new Promise((resolve,reject) => {
            console.log('7');
            resolve()
        }).then(() => {
            console.log('8');
        })
    </script>

.then执行的前提是上一个函数有执行结果

六:Web Worker(模拟多线程) H5提供了js多进程的实现  _____浏览器输出报错。。。了解即可

1.自定义Worker.js文件内不执行输出语句

2.相关API

              * Worker: 构造函数, 加载分线程执行的js文件           

              * Worker.prototype.postMessage(): 主进程和子线程相互发送消息

              * Worker.prototype.onmessage = function(event){}: 用于接收另一个线程的回调函数;onmessage里面有事件对象event,event上面有一个属性叫做data,用来接收主线程传递过来的消息


 

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值