尚硅谷---JS高级

一、基础总结深入

1.1 数据类型

1.分类:

        基本(值)类型

                ·String

                ·Number

                ·Boolean

                ·undefined

                ·null

        对象(引用)类型

                ·Object

                ·Function        一种特别的对象(可以执行)

                ·Array             一种特别的对象(数值下标,内部数据是有序的)

2.判断

        ·typeof       

        ·instanceof       

        ·=== / ==

2.1 具体介绍

typeof        返回数据类型的字符串表达

        可以判断 undefined/数值/字符串/布尔值/null/function

        不能判断:null与object    object与array

var a;
console.log(a,typeof a,a===undefined,typeof a==='undefined');    
//undefined "undefined" true true
console.log(undefined==='undefined');    //false
a=3;
console.log(typeof a==='number');    //true
a='atguigu';
console.log(typeof a==='string');    //true
a=true;
console.log(typeof a==='boolean');    //true
a=null;
console.log(typeof a,a===null);    //object true

instanceof        判断对象的具体类型

        intanceof 实例

        a instanceof b  即判断a是否为b的实例

        var b1={
            b2:[1,'abc',console.log],
            b3:function(){
                console.log("b3");
                return function (){
                    return 'xfz';
                }
            }
        }
        console.log(b1 instanceof Object); //true
        console.log(b1 instanceof Array);  //false
        console.log(b1.b2 instanceof Object,b1.b2 instanceof Array);//true,true
        console.log(b1.b3 instanceof Function,b1.b3 instanceof Object);//true,true
        console.log(typeof b1.b3==='function'); //true
        console.log(b1.b2[2],typeof b1.b2[2]==='function')//function log() { [native code] } true
        b1.b2[2]("我是一个函数"); //我是一个函数
        console.log(b1.b3()());//b3  xfz
        console.log(typeof b1.b2);//object

1.1.1 相关问题

实例和类型的区别:

实例:实例对象

类型:类型对象

function Person(name,age){
            this.name=name;
            this.age=age;
        }   //构造函数,类型
        var p=new Person("张铁三",19); //根据类型创建的实例对象
    

1.undefined和null的区别?

        undefined代表定义未赋值

        null代表定义并赋值了,只是值为null

2.什么时候给变量赋值为null?

        var b=null;  //初始赋值为null,表明将要赋值为对象

        b=['atguigu',12];  //确定对象时就赋值

        b=null; //让b指向的对象称为垃圾对象,从而被浏览器(垃圾回收器)回收

3.严格区分变量类型和数据类型?

        数据的类型

                分为基本类型和对象类型

        变量的类型(变量内存值的类型)

                分为基本类型和引用类型

                基本类型:保存的就是基本类型的数据

                引用类型:保存的是地址值

1.2 数据、变量、内存

1.什么是数据?

        存储在内存中,代表特定的信息,本质上是01010101……

        数据的特点:可传递、可运算

        一切皆数据

        内存中所有操作的目标:数据

                算术运算、逻辑运算、赋值、运行函数

2.什么是内存?

         内存条通电后产生的可存储数据的空间(临时的)

        内存的产生和死亡:

                内存条(电路板)==>通电==>产生内存空间==>存储数据==>处理数据==>断电==>内存空间和数据消失

        一块小内存的2个数据:

                内部存储的数据

                地址值

        内存分类:

                栈:全局变量/局部变量

                堆:对象

3.什么是变量?

        可变化的量,由变量名和变量值组成

        每个变量都对应一块小内存,变量名用来查找对应的内存,变量值就是内存中保存的数据

4.内存、数据、变量三者之间的关系?

        内存是用来存储数据的空间

1.2.1 相关问题(1)

 1.var a=xxx;a内存中到底保存的是什么?

        xxx是基本数据,保存的就是这个数据

        xxx是对象,保存的就是对象的地址值

        xxx是一个变量,保存的是xxx的内存内容(可能是基本数据,也可能是地址值)

2.关于引用变量赋值的问题

        n个引用变量指向同一个对象,通过一个变量修改对象内部数据,另外所以变量看到的是修改之后的数据

        var obj1={name:"Tom"};
        var obj2=obj1;
        obj1.name="Jack";
        console.log(obj2.name);//Jack

        2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象

        var a={age:12};
        var b=a;
        a={name:'bob',age:13};
        console.log(b.age,a.name,a.age);//12 bob 13
        function fn(obj){
            obj={age:15};
        }
        fn(a);
        console.log(a.age);//13

a.age不变的原因:

        将a传入以后,obj和a都指向对象{name:"bob",age:13};

        而赋值操作obj={age:15}; 是将新的对象赋值给obj,即断开obj和原对象{name:"bob",age:13}的关系,不会影响a和对象{name:"bob",age:13}的关系

1.2.2 相关问题(2)

问题:在js调用函数传递变量参数时,是值传递还是引用传递?

        理解1:都是值(基本值/地址值)传递

        理解2:可能是值传递,也可能是引用传递(地址值)

        var a=3;
        function fn(a){
            a=a+1;
        }
        fn(a);
        console.log(a);//3
        function fn2(obj){
            console.log(obj.name);
        }
        var obj={name:"Tom"};
        fn2(obj); //Tom

问题:JS引擎如何管理内存?

1.内存生命周期

        分配空间,得到它的使用权

        存储数据,可以反复进行操作

        释放空间

2.释放内存

        局部变量:函数执行完自动释放

        对象:成为垃圾对象==>由垃圾回收器回收 

b是自动释放,b所指向的对象是在后面的某个时刻由垃圾回收器回收

        function fn3(){
            var b={};
        }
        fn3();

1.3 对象

1.什么是对象?

        多个数据的封装体

        用来保存多个数据的容器

        一个对象代表现实中一个事物

2.为什么要用对象?

        统一管理多个数据

3.对象的组成

        属性:属性名(字符串)和属性值(任意类型)组成

        方法:一种特别的属性(属性值是函数)

       var p={
           name:'tom',
           age:12,
           setName:function(name){
               this.name=name;
           },
           setAge:function(age) {
               this.age = age;
           }
       }
       console.log(p.name,p.setName("bob"),p.name);
       //tom undefined bob
        p["setAge"](23);
       console.log(p.age); //23

4.如何访问对象内部数据?

        .属性名:编码简单,但有时不能用

        ['属性名']:编码麻烦,能通用

5.什么时候必须使用['属性名']的方式?

        5.1 属性名包含特殊字符:- 和 空格

       var o={};
       //o.content-type="text-json";
       o['content-type']="text-json";

        5.2 属性名不确定

       var propName='myAge';
       var value=18;
       // o.propName=value;
       o[propName]=value;
       console.log(o.myAge); //18

1.4 函数

        function showInfo(age){
            if(age<18) console.log("未成年,等等吧~");
            else if(age>60) console.log("颐养天年吧~");
            else console.log("刚好~");
        }
        showInfo(45); //刚好~

1.什么是函数?

        实现特定功能的n条语句的封装体

        只有函数是可以执行的,其他类型的数据不能执行

2.为什么要用函数?

        提高代码复用

        便于阅读交流

3.如何定义函数?

        函数声明

        表达式

        function fn(){ //函数声明
            console.log('fn()');
        }
        var fn2=function(){ //表达式
            console.log('fn2()');
        }

4.如何调用(执行)函数?

        test():直接调用

        obj.test():通过对象调用

        new test():new调用

        test.call(obj)/test.apply(obj):相当于obj.test(),临时让test成为obj的方法进行调用

                可以让一个函数成为指定的任意对象的方法进行调用

        var obj={};
        function test(){
            this.name='atguigu';
        }
        test.call(obj);
        console.log(obj);//{name:"atguigu"}

1.5 回调函数

        window.onload=function(){
            document.getElementById("btn").onclick=function () {//dom事件回调函数
                alert(this.innerHTML);
                }
                setTimeout(function(){ //定时器回调函数
                    alert("到点了~")
                },2000);
        }

1.什么函数时回调函数?

        1)你定义的

        2)你没有调

        3)最终它执行了

2.常见的回调函数?

        1)dom事件回调函数

        2)定时器回调函数

        3)ajax请求回调函数

        4)生命周期回调函数

1.6 IIFE

全称:Immediately-Invoked Function Expression

作用:

        隐藏实现

        不会污染外部(全局)命名空间

            (function(){
                var a=1;
                function test() {
                    console.log(++a);
                }
                window.$=function (){ //向外暴露一个全局函数
                    return{
                        test:test
                    }
                }
            })()
            $().test();

$是一个函数

$执行后返回的是一个对象

1.7 函数中的this

1.this是什么?

任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window

所有函数内部都有一个变量this

它的值是调用函数的当前对象

2.如何确定this的值?

test():window

p.test():p

new test():新创建的对象

p.call(obj):obj

            function Person(color){
                console.log(this);
                this.color=color;
                this.getColor=function(){
                    console.log(this)
                    return this.color;
                };
                this.setColor=function(color){
                    console.log(this);
                    this.color=color;
                };
            }
            Person("red"); //this是window
            var p=new Person("yellow"); //this是p
            p.getColor();//this是p
            var obj={};
            p.setColor.call(obj,"black"); //this是obj
            var test=p.setColor;
            test(); //this是window
            function fun1(){
                function fun2(){
                    console.log(this);//this是window
                }
            }

1.8 关于语句分号的问题

什么情况下必须加分号?

        1.小括号开头的前一条语句

        2.中方括号开头的前一条语句

二、函数高级

2.1原型与原型链

2.1.1 原型

1.函数的prototype属性

        每个函数都有一个prototype属性,它默认指向一个Object空对象(即成为:原型对象)

        console.log(typeof Date.prototype) //object

        原型对象中有一个属性constructor,它指向函数对象

2.给原型对象中添加属性(一般是方法)

        作用:函数的所有实例对象自动拥有原型中的属性(方法)

        console.log(Date.prototype)
        console.log(typeof Date.prototype) //object
        function fn(){
            console.log("我是函数")
        }
        console.log(fn.prototype)
        fn.prototype.test=function(){}
        console.log(fn.prototype)
        console.log(Date.prototype.constructor===Date)
        var obj=new fn();
        obj.test();

2.1.2 显示原型与隐式原型

1.每个函数function都有一个prototype,即显示原型(属性)

2.每个实例对象都有一个__proto__,即隐式原型(属性)

3.对象的隐式原型的值为其构造函数的显示原型的值

        function fn(){} //内部语句:this.prototype={}
        console.log(fn.prototype) //Object{constructor:Function}
        var f=new fn() //内部语句:this.__proto__=fn.prototype
        console.log(f.__proto__) //Object{constructor:Function}
        console.log(fn.prototype===f.__proto__) //true

4.内存结构

        fn.prototype.test=function(){
            console.log("我是新添加的方法")
        }
        f.test() //我是新添加的方法

 

5.总结

函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象

对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值

程序员能直接操作显示原型,但不能直接操作隐式原型(es6之前) 

2.1.3 原型链

1.原型链

访问一个对象的属性时

        现在自身属性中查找,找到返回

        如果没有,再沿着__proto__这条链网上查找,找到返回

        如果最终没找到,返回undefined

别名:隐式原型链

作用:查找对象的属性(方法)

        function Fn(){
            this.test1=function(){
                console.log('test1')
            }
        }
        Fn.prototype.test2=function(){
            console.log('test2')
        }
        var fn=new Fn();
        fn.test1() //test1
        fn.test2() //test2
        console.log(fn.toString()) //[object Object]
        console.log(fn.test3) //undefined

补充问题:

1.函数的显式原型指向的对象:默认是空Object的实例对象(但Object自身不满足)

        console.log(Fn.prototype instanceof Object) //true
        console.log(Object.prototype instanceof Object) //false
        console.log(Function.prototype instanceof Object) //true
        

 2.Function是它自身的实例

        所有函数都是Function的实例(包括Function本身)

        console.log(Function.prototype===Function.__proto__) //true

3.Object的原型对象是原型链的尽头

        console.log(Object.prototype.__proto__) //null

2.1.4 属性问题

读取对象的属性值时:会自动到原型链中查找

设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值

        function Fn(){}
        var f1=new Fn();
        Fn.prototype.a='xxx';
        console.log(f1.a,f1);
        var f2=new Fn();
        f2.a='yyy';
        console.log(f1.a,f2.a,f2);

 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上

       function Person(name,age){
           this.name=name;
           this.age=age;
       }
       Person.prototype.setName=function(name){
           this.name=name;
       }
       var p1=new Person("Tom",12);
       p1.setName("bob");
       console.log(p1); //Person{name:bob,age:12}

       var p2=new Person("Jack",12);
       // p2.setName("Cat");
       console.log(p2); //Person{name:"Jack",age:12}

       console.log(p1.__proto__===p2.__proto__);//true

2.1.5 探索instanceof

1.instanceof是如何判断的?

表达式:A    instanceof   B

如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false

       function Foo(){}
       var f1=new Foo();
       console.log(f1 instanceof Foo);     //true
       console.log(f1 instanceof Object);  //true

2.Function是通过new自己产生的实例

       console.log(Object instanceof Function);  //true
       console.log(Object instanceof Object);    //true
       console.log(Function instanceof Function); //true
       console.log(Function instanceof Object);   //true

2.1.6 面试题

测试题1

       function A(){}
       A.prototype.n=1;
       var b=new A();
       A.prototype={
           n:2,
           m:3
       }
       var c=new A();
       console.log(b.n,b.m,c.n,c.m); //1 undefined 2 3

测试题2

        function F(){}
        Object.prototype.a=function(){
            console.log('a()')
        }
        Function.prototype.b=function(){
            console.log('b()')
        }
        var f=new F()
        f.a()   //a()
        f.b()   //Uncaught TypeError:f.b is not a function

        F.a()   //a()
        F.b()   //b()

2.2 执行上下文与执行上下文栈

2.2.1 变量提升与函数提升

1.变量声明提升

通过var定义(声明)的变量,在定义语句之前就可以访问到

值:undefined

2.函数声明提升

通过function声明的函数,在之前就可以直接调用

值:函数定义(对象)

        fn()    //可调用,函数提升
        fn3()   //不可调用,变量提升
        var a=3
        function fn(){
            console.log('fn()执行') //fn()执行
            console.log(a)  //undefined
            var a=4
        }
        var fn3=function(){
            console.log('fn3()') //Uncaught TypeError:fn3 is not a function
        }

2.2.2 执行上下文

1.代码分类(位置)

        全局代码

        函数(局部)代码

2.全局执行上下文

        在执行全局代码前将window确定为全局执行上下文

        对全局数据进行预处理

                var定义的全局变量==>undefined,添加为window的属性

                function声明的全局函数==>赋值(fun),添加为window的方法

                this==>赋值(window)

        开始执行全局代码

        console.log(a1,window.a1) //undefined undefined
        a2()    //a2()
        console.log(this)  //Window{window:Window,self:Window...}
        var a1=3;
        function a2(){
            console.log('a2()')
        }

3.函数执行上下文

        在调用函数、准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)

        对局部数据进行预处理:

                形参变量==>赋值(实参)==>添加为执行上下文的属性

                arguments==>赋值(实参列表),添加为执行上下文的属性

                var定义的局部变量==>undefined,添加为执行上下文的属性

                this==>赋值(调用函数的对象)

        开始执行函数体代码

        function fn(a1){
            console.log(a1)  //5
            console.log(a2)  //undefined
            a3()             //a3()
            console.log(this)  //Window
            console.log(arguments) //Arguments(3)[5,6,7,Function,Function]
            var a2=3
            function a3(){
                console.log('a3()')
            }
        }
        fn(5,6,7)

2.2.3 执行上下文栈

1.在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象

2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)

3.在函数执行上下文创建后,将其添加到栈中(压栈)

4.在当前函数执行完后,将栈顶的对象移除(出栈)

5.当所有的代码执行完后,栈中只剩下window

                    //1.进入全局执行上下文
        var a=10
        var bar=function(x){
            var b=5
            foo(x+b)            //3.进入foo执行上下文
        }
        var foo=function(y){
            var c=5;
            console.log(a+c+y)  //30
        }
        bar(10)                //2.进入bar函数执行上下文

2.2.4 面试题

测试题1:

        console.log('global begin:'+i)
        var i=1
        foo(1)
        function foo(i){
            if(i==4) return
            console.log('foo() begin:'+i)
            foo(i+1)    //递归调用,在函数内部调用自己
            console.log('foo() end:'+i)
        }

        console.log('ge:'+i)

1.依次输出什么?

2.整个过程中产生了几个执行执行上下文?

5个

测试题2:

先执行变量提升,再执行函数提升

        function a(){}
        var a
        console.log(typeof a) //function

测试题3:

        if(!(b in window)){
            var b=1
        }
        console.log(b)  //undefined

程序会首先解析所有声明的函数,其次是var声明的变量,因为javascript没有块的概念,所以if(){...}中,var声明了 b = 1, b是依然属于全局变量。

执行等价于:

var b;
if(!(b in window)){
    b=1;
}
console.log(b);

(1)开始时,声明了变量b,但并没有赋值,所以b = undefined ,  而undefined 存在于window中,所以(b in window)返回true,  取反为false, 这样就不会执行大括号里面的 “b=1” 的语句。

(2) console.log(b); //undefined

测试题4:

        var c=1
        function c(c){
            console.log(c)
            var c=3
        }
        c(2) //Uncaught TypeError:c is not a function

执行为:

var c
function c(c){
    var c
    console.log(c)
    c=3
}
c=1
c(2)

因此会报错

2.3 作用域与作用域链

作用域

1.理解

        就是一块地盘,一个代码所在的区域

        它是静态的(相对于上下文对象),在编写代码时就确定了

2.分类

        全局作用域

        函数作用域

        没有块作用域(ES6有了)

3.作用

        隔离变量,不同作用域下同名变量不会有冲突

作用域和执行上下文区别:

区别1:

        全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时确定

        全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建

        函数执行上下文是在调用函数时,函数体代码执行之前创建

区别2:

        作用域是静态的,只要函数定义好了就一直存在,且不会在变化

        上下文环境是动态的,调用函数时创建,函数调用结束时上下文环境就会被释放

联系:

        上下文环境(对象)是从属于所在的作用域

        全局上下文环境==>全局作用域

        函数上下文环境==>对应的函数作用域

作用域链:

2.3.1 面试题(1) 

       var x=10
       function fn(){
           console.log(x)
       }
       function show(f){
           var x=20
           f()
       }
       show(fn) //10

因为函数的作用域是静态的,是一开始就确定的,不是在后面调用的时候动态的去寻找的

2.3.2 面试题(2)

        var fn=function (){
            console.log(fn)
        }
        fn()    //function(){...}

        var obj={
            fn2:function (){
                console.log(fn2)
            }
        }
        obj.fn2() //Uncaught ReferenceError:fn2 is not defined

2.4 闭包

2.4.1 循环遍历加监听 

循环中如果判断条件是i<btns.length,则每次判断都会计算一次btns.length,为了提高效率,应该写为:

for(var i=0,length=btns.length;i<length;i++){...}

因为在点击的时候,for循环早就执行完了,所以需要在点击事件前给每一个btn挂上index

            var btns = document.getElementsByTagName("button");
            //遍历加监听
            for(var i=0,length=btns.length;i<length;i++){
                btns[i].index=i
                btns[i].onclick=function(){
                    alert("第"+(this.index+1)+"个button")
                }
            }

也可以利用闭包:

            var btns = document.getElementsByTagName("button");
            //遍历加监听
            for(var i=0,length=btns.length;i<length;i++){
                (function(i){
                    btns[i].onclick=function (){
                        alert("第"+(i+1)+"个")
                    }
                })(i)
            }

2.4.2 闭包理解

1.如何产生闭包?

        当一个嵌套的内部(子)函数引用嵌套了外部(父)函数的变量(函数)时,就产生了闭包

2.使用chrome调试查看

        理解一:闭包是嵌套的内部函数(绝大部分人)

        理解二:包含被引用变量(函数)的对象(极少数人)

        注意:闭包存在于嵌套的内部函数中

3.产生闭包的条件?

        函数嵌套

        内部函数引用了外部函数的数据(变量/函数)

执行函数定义就会产生闭包(不用调用内部函数)

2.4.3 常见的闭包

1.将函数作为另一个函数的返回值

        function fn1(){
            var a=2
            function fn2(){
                a++
                console.log(a)
            }
            return fn2
        }
        var f=fn1()
        f() //3
        f() //4
        f() //5
        var f2=fn1()
        f2()  //3
        f()   //6

2.将函数作为实参传递给另一个函数调用

        function showDelay(msg,time){
            setTimeout(function(){
                alert(msg)
            },time)
        }
        showDelay('atguigu',2000)

2.4.4 闭包的作用

1.使用函数内部的变量,在函数执行完后,任然存活在内存中(延长了局部变量对生命周期)

2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

        1.函数执行完后,函数内部声明的局部变量是否还存在?

                一般是不存在,存在于闭包中的变量才可能存在

        2.在函数外部能直接访问函数内部的局部变量吗?

                不能,但我们可以通过闭包让外部操作它

2.4.5 闭包的生命周期

1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)

2.死亡:在嵌套的内部函数成为垃圾对象时 

        function fn1(){
            //此时闭包fn2就已经产生了(函数提升,内部函数对象已经创建了)
            var a=2
            function fn2(){
                a++
                console.log(a)
            }
            var fn3=function(){ //闭包fn3在本行才产生
                a--
                console.log(a)
            }
            return fn2
        }
        var f=fn1()
        f() //3
        f() //4
        f=null //闭包fn2死亡(包含闭包的函数对象称为垃圾对象)

2.4.6 闭包的应用

闭包的应用:定义JS模块

具有特定功能的js文件

将所有的数据和功能都封装在一个函数内部(私有的)

指向外部暴露一个包含n个方法的对象或函数

模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

第一种写法:

function myModule(){
    //私有数据
    var msg='MY atguigu'
    function doSth(){
        console.log('doSth():'+msg.toUpperCase())
    }
    function doOtherThing(){
        console.log('doOhterThing():'+msg.toLowerCase())
    }
    //向外暴露对象(给外部使用的方法)
    return {
        doSth:doSth,
        doOtherThing:doOtherThing
    }
}
        var fn=myModule()
        fn.doSth() //doSth():MY ATGUIGU
        fn.doOtherThing()   //doOhterThing():my atguigu

第二种写法:

(function myModule(){
    var msg='My Lover'
    function fn1(){
        console.log('fn1():'+msg.toUpperCase())
    }
    function fn2(){
        console.log('fn2():'+msg.toLowerCase())
    }
    window.myModule2={
        fn1:fn1,
        fn2:fn2
    }
})()
        window.myModule2.fn1()  //fn1():MY LOVER
        window.myModule2.fn2() //fn2():my lover

2.4.7 内存溢出与内存泄漏

1.缺点

函数执行完后,函数内的局部变量没有释放,占用内存时间会变长

容易造成内存泄漏

2.解决

能不用闭包就不用

及时释放

1.内存溢出

        一种程序运行出现的错误

        当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误

2.内存泄漏

        占用的内存没有及时释放

        内存泄漏累积多了就容易导致内存溢出

常见的内存泄漏:

        意外的全局变量

        没有及时清理的计时器或回调函数

        闭包         

2.4.8 面试题

      var name='The Window'
      var object={
          name:"My Object",
          getNameFunc:function(){
              return function(){
                  return this.name
              }
          }
      }
      console.log(object.getNameFunc()()) //The Window

      var name2="The Window2"
      var object2={
          name2:"My Object2",
          getNameFunc:function(){
                var that=this
                return function (){
                    return that.name2
                }
          }
      }
      console.log(object2.getNameFunc()()) //My Object2

 三、面向对象高级

3.1 对象创建模式

方式一:Object构造函数模式

        套路:先创建空Object对象,再动态添加属性/方法

        适用场景:起始时不确定对象内部数据

        问题:语句太多

        var p=new Object()
        p.name='tom'
        p.age=12
        p.setName=function(name){
            this.name=name
        }
        p.setName('jack')
        console.log(p) //Object {name: "jack", age: 12, setName: Function}

方式二:对象字面量模式

        套路:使用{}创建对象,同时指定属性/方法

        适用场景:起始时对象内部数据是确定的

        问题:如果创建多个对象,有重复代码

       var p={
           name:'tom',
           age:12,
           setName:function(name){
               this.name=name
           }
       }
       console.log(p.name,p.age,p.setName("jACK"),p)
       //tom 12 undefined
       //Object {name: "jACK", age: 12, setName: Function}

方式三:工厂模式

        套路:通过工厂函数动态创建对象并返回

        适用场景:需要创建多个对象

        问题:对象没有一个具体的类型,都是Object类型 

        function createPerson(name,age){
            var obj={
                name:name,
                age:age,
                setName:function(name){
                    this.name=name
                }
            }
            return obj
        }
        //Object {name: "Tom", age: 12, setName: Function}
        console.log(createPerson("Tom",12))
        //Object {name: "Jack", age: 40, setName: Function}
        console.log(createPerson("Jack",40))

方式四:自定义构造函数模式

        套路:自定义构造函数,通过new创建对象

        适用场景:需要创建多个类型确定的对象

        问题:每个对象都有相同的数据,浪费内存

       function Person(name,age){
           this.name=name
           this.age=age
           this.setName=function (name){
               this.name=name
           }
       }
        //Person {name: "Bob", age: 43, setName: Function}
       console.log(new Person("Bob",43))

优化写法,构造函数中一般只初始化属性:

       function Person(name,age){
           this.name=name
           this.age=age
       }
       Person.prototype.setName=function(name){
           this.name=name
       }

3.2 继承模式

3.2.1 原型链继承

方式一:使用原型链继承

1.定义父类型构造函数

2.给父类型的原型添加方法

3.定义子类型的构造函数

4.创建父类型的对象赋值给子类型的原型

5.将子类型原型的构造属性设置为子类型

6.给子类型原型添加方法

7.创建子类型的对象:可以调用父类型的方法

关键:子类型的原型为父类型的一个实例对象

        //父类型
       function Supper(){
           this.supProp='Supper property'
       }
       Supper.prototype.showSupProp=function(){
           console.log(this.supProp)
       }
       function Sub(){
           this.subProp='Sub property'
       }
       //子类型的原型为父类型的一个实例对象
       Sub.prototype=new Supper()
       Sub.prototype.showSubProp=function(){
           console.log(this.subProp)
       }

       var sub=new Sub()
        sub.showSubProp() //Sub property
        sub.showSupProp() //Supper property

3.2.2 组合继承

由于Sub的prototype指向Supper的实例对象,所以Sub的构造函数时Supper的构造函数,为了使Sub拥有自己的构造函数实现完整统一,可以进行如下修改

        console.log(sub.constructor) //function Supper(){}
        Sub.prototype.constructor=Sub
        console.log(sub.constructor) //function Sub(){}

方式二:借用构造函数继承

1.定义父类型构造函数

2.定义子类型构造函数

3.在子类型构造函数中调用父类型构造

关键:崽子类型构造函数中通用supper()调用父类型构造函数

        function Person(name,age){
            this.name=name
            this.age=age
        }
        function Student(name,age,price){
            Person.call(this,name,age)
            this.price=price
        }
        var s=new Student('Tom',20,2400)
        console.log(s.name,s.age,s.price) //Tom 20 2400

方式三:原型链+借用构造函数的组合继承

1.利用原型链实现对父类型对象的方法继承

2.利用super()借用父类型构造函数初始化相同属性

        function Person(name,age){
            this.name=name
            this.age=age
        }
        Person.prototype.setName=function(name){
            this.name=name
        }
        function Student(name,age,price){
            Person.call(this,name,age)  //为了得到属性
            this.price=price
        }
        Student.prototype=new Person() //为了能看到父类型的方法
        Student.prototype.constructor=Student //修正constructor属性
        Student.prototype.setPrice=function(price){
            this.price=price
        }
        var s=new Student('Tom',20,2400)
        s.setName('Bob')
        s.setPrice(3000)
        console.log(s.name,s.age,s.price) //Tom 20 2400

3.2.3 闭包终极面试题

       function fun(n,o){
           console.log(o)
           return {
               fun:function(m){
                   return fun(m,n)
               }
           }
       }

       var a=fun(0) //undefined
       a.fun(1)  //0
       a.fun(2)  //0
       a.fun(3)  //0

       var b=fun(0).fun(1).fun(2).fun(3) //undefined 0 1 2

       fun(0).fun(1).fun(2).fun(3) //undefined 0 1 2

       var c=fun(0).fun(1) //undefined 0
       c.fun(2) //1
       c.fun(3) //1

四、线程机制与事件机制

4.1 进程与线程

进程(process):程序的一次执行,他占有一片独有的内存空间

                可以通过windows任务管理器查看进程(快捷键:ctrl+shift+esc)

线程(thread):是进程内的一个独立执行单元

                        是程序执行的一个完整流程

                        是CPU的最小调度单元

相关知识:

 应用程序必须运行在某个进程的某个线程上

一个进程中至少有一个运行的线程:主线程,进程启动后自动创建

一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的

一个进程内的数据可以供其中多个线程直接共享

多个进程之间的数据是不能直接共享的

线程池(thread pool):保存多个线程对象的容器,实现线程对象的反复利用

相关问题:

何为多进程与多线程?

        多进程运行:一个应用程序可以同时启动多个实例运行

        多线程:在一个进程内,同时有多个线程运行

比较单线程与多线程?

        多线程:

                优点:能有效提升CPU利用率

                缺点:创建多线程开销

                           线程间切换开销

                           死锁与状态同步问题

        单线程:

                优点:顺序编程简单易懂

                缺点:效率低

JS是单线程还是多线程?

        js是单线程运行的

        但是用H5中的Web Workers可以多线程运行

浏览器运行是单线程还是多线程?

        都是多线程运行的

浏览器运行是单进程还是多进程?

        有的是单进程(Firefox),有的是多进程(Chrome)

4.2 浏览器内核

支撑浏览器运行的最核心的程序

不同的浏览器可能不一样

        Chrome/Safari:webkit

        ifrefox:Gecko

        IE:Trident

        360、搜狗等国内浏览器:Trident+webkit

内核由很多模块组成

4.3 定时器引发的思考

1.定时器真的是定时执行的吗?

        定时器并不能保证真正定时执行

        一般会延迟一点,也有可能延迟很长时间

2.定时器回调函数是在分线程执行的吗?

        在主线程执行的,js是单线程的

3.定时器是如何实现的?

        事件循环模型

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./2.js" type="text/javascript"></script>
    <script>
        window.onload=function(){
            var btn=document.getElementById("btn01")
            btn.onclick=function(){
                console.log('启动定时器前...')
                var start=Date.now()
                setTimeout(function(){
                    console.log('定时器执行了'+(Date.now()-start)+'ms')
                },2000)
                console.log('定时器启动后...')
            }
            /*
            *执行结果:
            *启动定时器前...
            *定时器启动后...
            *定时器执行了2019ms
            *
            *
            * */
        }
    </script>
</head>
<body>
    <button id="btn01">测试点击事件</button>

</body>
</html>

4.4 js是单线程执行的

1.如何证明js执行是单线程的?

        setTimeout()的回调函数是主线程执行的

        定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./2.js" type="text/javascript"></script>
    <script>
        window.onload=function(){
            var btn=document.getElementById("btn01")
            btn.onclick=function() {
                setTimeout(function(){
                    console.log('timeout 2222')
                },2000)
                setTimeout(function(){
                    console.log('timeout 1111')
                },1000)
                function fn(){
                    console.log('fn()')
                }
                fn()
                console.log('alert()之前')
                alert('--------') //暂停当前主线程的执行,同时暂停计时
                //点击确定后,恢复程序执行和计时
                console.log('alert()之后')
                /*
                * 在alert弹出后,不点确定,控制台输出:
                * fn()
                * alert()之前
                * */
            }
        }
    </script>
</head>
<body>
    <button id="btn01">测试点击事件</button>

</body>
</html>

2.为什么js要用单线程模式,而不是多线程模式?

        JavaScript的单线程,与它的用途有关

        作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM

        这决定了它只能是单线程,否则会带来很复杂的同步问题

3.代码的分类:

        初始化代码

        回调代码

4.js引擎执行代码的基本流程

        先执行初始化代码:包含一些特别的代码

                设置定时器

                绑定监听

                发送ajax请求

        后面在某个时刻才会执行回调代码

在代码首部添加下面的代码

                setTimeout(function(){
                    console.log('timeout 0000')
                },0)

运行截图:

回调函数为异步执行,即当初始化代码执行完毕后,才会执行

4.5 事件循环模型

1.所有代码分类

        初始化执行代码(同步代码):包含绑定dom事件监听,设置定时器,发送ajax请求的代码

        回调执行代码(异步代码):处理回调逻辑

2.js引擎执行代码的基本流程

        初始化代码---->回调代码

3.模型的2个重要组成部分

        事件(定时器/DOM事件/Ajax)管理模块

        回调队列

4.模型的运转流程

        执行初始化代码,将事件回调函数交给对应模块管理

        当事件发生时,管理模块会将回调函数及其数据添加到回调队列中

        只有当初始化代码完成后(可能需要一定时间),才会遍历读取回调队列中的回调函数执行

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./2.js" type="text/javascript"></script>
    <script>
        window.onload=function(){
            function fn1(){
                console.log('fn1()')
            }
            fn1()
            var btn=document.getElementById("btn01")
            btn.onclick=function() {
                console.log("点击了btn")
            }
            setTimeout(function(){
                console.log("定时器执行了")
            },2000)
            function fn2(){
                console.log('fn2()')
            }
            fn2()
            /*
            *fn1()
            *fn2()
            *定时器执行了
            *点击了btn
            * */
        }
    </script>
</head>
<body>
    <button id="btn01">测试点击事件</button>

</body>
</html>

相关重要概念:

1.执行栈

        execution stack

        所有的代码都是在此空间中执行的

2.浏览器内核

        browser core

        js引擎模块(在主线程中处理)

        其他模块(在主/分线程处理)

        运行原理图

3.任务队列        task queue

4.消息队列        message queue

5.事件队列        event queue

        3.4.5 同一个callback queue

6.事件轮询        event loop

        从任务队列中循环取出回调函数放入执行栈中处理(一个接一个)

7.事件驱动模型

        event-driven interaction model

8.请求响应模型

        request-response model

4.6 H5 Web Workers(多线程)

1.H5规范提供了js分线程的实现,取名为Web Workers

        我们可以将一些大计算量的代码交由web workers运行而不冻结用户界面

        但是子程序完全受主线程控制,且不得操作DOM

        所以,这个新标准并没有改变JavaScript单线程的本质

2.相关API

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

        Worker.prototype.onmessage:用于接收另一个线程的回调函数

        Worker.prototype.postMessage:向另一个线程发送消息

3.不足

        worker内代码不能操作DOM(更新UI)

        不能跨域加载JS

        不是每个浏览器都支持这个新特性

分线程中的全局对象不再是window,所以在分线程中不可能更新界面

alert是window的方法,在分线程中不能调用

function fibonacci(n){
    return n<=2?1:fibonacci(n-1)+fibonacci(n-2) //递归调用
}
var onmessage=function(event){
    var number=event.data
    console.log('分线程接收到主线程发送的数据'+number)
    //计算
    var result=fibonacci(number)
    postMessage(result)
    console.log('分线程向主线程返回数据:'+result)
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input id="number" type="text" placeholder="数值"></input>
    <button id="btn">计算</button>
    <script>
        var input=document.getElementById('number')
        document.getElementById('btn').onclick=function(){
            var number=input.value
            //创建一个worker对象
            var worker=new Worker('1.js')
            //向分线程发送消息
            worker.postMessage(number)
            console.log('主线程向分线程发送数据'+number)
            //绑定接收消息的监听
            worker.onmessage=function(event){
                console.log('主线程接收分线程返回的数据'+event.data)
                alert(event.data)
            }
        }
    </script>
</body>
</html>


完结撒花❀❀~

祝大家学业有成~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值