JavaScript总结

数据类型

分类

基本数据类型

  • Null
    • null
  • Undefiend
    • undefined
  • String
    • 定义字符串:单引号/双引号/反引号 :转义符号
  • Number
    • 2进制 0b
    • 8进制 0o
    • 16进制 0x
    • 10进制
    • 科学计数法 1.23E-30
    • NaN
    • Infinity
  • Boolean
    • true/false

引用数据类型

  • Object
    • Object 对象{} 无序复杂的对象数据类型
    • Array 数组[] 有序的简单的对象数据类型
    • Function 函数 特殊的可执行的对象类型
    • Math/Date/RegExp
    • String/Number/Boolean的包装类型

变量/数据类型

变量类型和数据类型:

数据类型

什么是数据,就是‘abc’ {} [] true 类似的数据

  • 数据类型
    • 基本数据类型
    • 对象数据类型(单指数据来说,本身就是一个对象类型)

变量类型

什么变量:我们创建的代表数据的一个标识符

  • 变量类型
    • 基本数据类型 变量的值是一个基本值
    • 引用数据类型 变量的值是一个地址值(只有变量才会出现引用关系)
var a = 1; //变量a是基本类型   1是基本数据类型
var b = [1, 2]; //变量b是引用类型    [1,2]是对象类型

判断数据类型

typeof判断

返回当前数据的类型字符的字符串

typeof有两种用法 typeof(value) / typeof value

可以检测的类型

  1. number
  2. boolean
  3. string
  4. undefined
  5. Function
  6. object(只能检测出来是对象类型,不能检测出是哪一种对象)
console.log(log);  // ƒ log() { [native code] }
var log = console.log;
log(typeof 123); //number
log(typeof "123"); //string
log(typeof (null)); //object
log(typeof undefined); //undefined
log(typeof []); //object
log(typeof {}); //object
log(typeof true); //boolean
log(typeof function () {}); //Function
log(typeof Math); //Object  Math直接就是对象,不需要实例化才运行
log(typeof Date); //Function
log(typeof new Date()); //Object

instanceof

如果B的显式原型对象 在 A的原型链上(通过__proto__确定的) 则返回true

A instanceof B:

​ B的原型对象在A的原型链上

主要用来检测对象类型,对于基本类型来说不能检测

可以检测的类型是:Array/Function/RegExp/Object

log([] instanceof Array); //true
log([] instanceof Object); //true
log(Array instanceof Function); //true
log([] instanceof Function); //false
log({} instanceof Array); //false
log("123" instanceof String); //false
log(1 insNumber); //false
log(true instanceof Boolean); //false
log(/\s/g instanceof RegExp) //true
log(new String("123") instanceof String); //true

===

因为null和undefined都只有一个唯一的值,null和undefined

所以可以通过全等 === 来检测这两个类型

var a = null;
var b;
log(b === undefined) //true
log(a === null) //true

var a = null;
console.log(a === null); //true

Object.prototype.toString.call()

其他的类型也有toString方法,但是和Object对象上的toString方法得到的结果不同

Object上的toString方法可以得到[object XXXX]的一个字符串 可以精确的确定对象的类类型

Object.prototype.toString.call(被检测的值)


undefined

console.log(console.log(1));

//1 undefined

在控制台打印的1 undefined 因为log函数没有返回值

垃圾对象

声明一个a是一个对象

在堆中创建一个对象

在栈中创建一个变量

把对象的地址赋值给栈中的变量

如果对象不用了,那么我们需要把对象给从内存中删除,这样节省空间,如果a是局部作用域的变量,那么函数执行完成的时候,a就会自动销毁,a销毁以后,对象就没有被变量所引用(此时对象已经不可能再被使用),此时对象就会变成垃圾对象,垃圾对象会等待回收

但是如果包含对象引用地址的变量是全局变量,则全局变量只有等待浏览器关闭才能销毁.所以此时不用的对象一直被引用,会占用空间.把变量主动设置为null,对象就没有被引用了,就变成了垃圾对象

内存

内存的生命周期

生命周期:一个东西 从出生到死亡的全过程

内存:通电 – 内存生效保存数据 – 断电(数据全部清空)

内存创建的时候:

堆空间:存放一些对象类型(大)

栈空间:存放基本类型和变量和地址值 (小)

内存要区分开:

运行内存和硬盘内存

变量就是代表内存的一块区域,通过变量就可以访问到这个区域


对象

是由一系列的键值对(key value)组成

key

一定是一个字符串

value

可以是任意类型的,其中如果值是function的话,对应的属性被称作为方法

点操作符

会把 点 后的值当做字符串解析 ,点后应该直接跟对象的属性

[]操作符

[]操作符(通用的):中括号中可以运算及直接解析变量,也可以直接书写对象的属性(必须是字符串格式)

[]使用场景

1.当key值是一个变量的时候

2.当key值是一个不规则字符的时候

创建对象

object/字面量方法

  • object构造函数创建/字面量的方法创建
  • 特点
    • 语句太多,每次创建一个都要执行一遍代码
		var car1 = new Object();
        var car1 = {};
        car1.color = "red";
        car1.speed = "300";
        car1.name = "BMW-X3";
        car1.run = function () {
            console.log("最高时速" + this.speed);
        } 

工厂模式(封装函数)

  • 特点
    • 无法区分当前对象是谁的,生成的对象没有具体的类型,都是Object
	  function car(name, color, speed) {
            var o = {};
            o.name = name;
            o.color = color;
            o.speed = speed;
            o.run = function () {
                console.log("最高时速" + this.speed);
            }
            return o;
        }

        var car1 = car("BMX-X1", "red", 300);
        var car2 = car("BMX-X3", "red", 330);

        console.log(car1 instanceof car); //false
        console.log(car1 instanceof Object); //false
        console.log(car1.constructor); //Object

构造函数模式

  • 使用构造函数+实例化
  • 特点
    • 所有的对象共同的方法都是各自私有的,占用内存
function Car(name, color, speed) {
            //如果当前的函数被new调用了,当前函数就是一个构造函数
            //构造函数中的this指向其实例化对象
            //构造函数不需要先创建一个对象,因为实例化的时候本身就创建了一个对象
            this.name = name;
            this.color = color;
            this.speed = speed;
            this.run = function () {
                console.log("最高时速" + this.speed);
            }
        }

        var car1 = new Car("BMX-X1", "red", 300);
        var car2 = new Car("BMX-X3", "red", 330);
        console.log(car1, car2);

        console.log(car1.run === car2.run) //false 

原型模式

  • 使用原型添加属性和方法
  • 特点
    • 无法给对象定制一些属性(无法传参)
function Car() {

        }
        Car.prototype.color = "red";
        Car.prototype.name = "BMX-X1";
        Car.prototype.speed = "200";
        Car.prototype.run = function () {
            console.log("最高时速" + this.speed);
        }

        var car1 = new Car();
        var car2 = new Car();
        console.log(car1.run === car2.run)

构造函数+原型

  • 构造函数可以定制属性(传参)
  • 原型对象可以书写共同的方法
 function Car(name, color, speed) {
            this.name = name;
            this.color = color;
            this.speed = speed;
        }
        Car.prototype.run = function () {
            console.log("最高时速" + this.speed);
        }

        var car1 = new Car("BMX-X1", "red", 300);
        var car2 = new Car("BMX-X3", "red", 330);

        console.log(car1 instanceof Car); //true
        console.log(car1.constructor); //Car
        console.log(car1.run === car2.run); //true

对象其它

公有属性/方法

  • 公有属性和公有方法
    • 设置给实例化对象的属性和方法被称作公有属性和公有方法

私有属性/方法

  • 私有属性和私有方法
    • 声明在构造函数中的变量或函数,被称作为私有属性和私有方法

静态属性/方法

  • js中无需实例化就可以调用的方法或属性叫做静态方法和静态属性

特权方法

  • 在构造函数中给实例对象绑定的方法被称为特权方法

示例

 function Car(color, speed) {
            //私有属性
            var num = 0;
            //私有方法
            function fn() {
                console.log("fn")
            }
            //这个属性是设置给所有实例化对象的(公有属性)
            this.color = color;
            this.speed = speed;

            //在构造函数中给实例化对象设置的方法被称作为 特权方法
            this.getColor = function () {
                console.log("特权方法")
            }
        }


        //这个方法是设置给所有实例化对象的(公有方法)
        Car.prototype.run = function () {
            console.log("驾~")
        }

        //就是简单的把Car当做一个对象(函数对象),扩展了属性和方法
        //这些属性和方法被称作为静态属性和静态方法
        Car.title = "造车";
        Car.do = function () {
            console.log("doit")
        }

面向对象

面向对象编程三大特性

封装

对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象就可以完成自己的操作。

1、良好的封装能够减少耦合。

2、类内部的结构可以自由修改。

3、可以对成员进行更精确的控制。

4、隐藏信息,实现细节。

继承

当一个对象能够使用另一个对象的属性和方法的时候,被称作为继承

简单继承
	var obj1 = {
            name: "lily",
            do: function () {
                console.log("eat")
            }
        }

        var obj2 = Object.create(obj1);
        obj2.sex = "nv"
        //obj2可以使用obj1的属性和方法,称作为继承
        console.log(obj2, obj1)
        console.log(obj2.name)
借用构造函数继承

只能继承构造函数中添加的属性和方法

  • 套路

1. 定义父类型构造函数

2. 定义子类型构造函数

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

/1.借用构造函数继承:只能继承构造函数中添加的属性和方法
        //封装一个父类
        function Animal(name, age) {
            this.name = name;
            this.age = age;
        }
        Animal.prototype.say = function () {
            cosnole.log("吼~")
        }

        //封装一个子类
        function Cat(color, name, age) {
            //本来Animal的this指向的是Animal的实例化对象
            //现在让Animal的this指向Cat的实例化对象
            //并且给Animal传参
            Animal.call(this, name, age)
            this.color = color;
        }
        Cat.prototype.do = function () {
            console.log("玩红外线");
        }


        var cat1 = new Cat("white", "baozi", 2);
        var cat2 = new Cat("coffce", "wanan", 3);
        console.log(cat1, cat2)
原型链继承
  • 套路

1. 定义父类型构造函数

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

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

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

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

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

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

  • 子类型的原型为父类型的一个实例对象
function Animal(name, age) {
            this.name = name;
            this.age = age;
        }
        Animal.prototype.say = function () {
            cosnole.log("吼~")
        }
        var ani = new Animal();
        ani.do = function () {

        }

        function Cat(color, name, age) {
            this.color = color;
        }
        //Cat的实例化对象想要使用Animal的原型对象上的方法,需要修改Cat的原型链
        //千万不要把Animal的原型对象直接赋值给Cat的原型对象,否则修改Cat Animal也修改了,因为赋值是地址值的赋值
        // Cat.prototype = Animal.prototype;
        //再次考虑,还有谁可以访问Animal的原型对象?Animal的实例化对象
        //把父类的实例化对象 赋值给 子类的原型对象  此时可以实现原型链继承
        Cat.prototype = new Animal();
        //如果重新设置了原型对象,那么新的原型对象的constructor就可能出问题
        //为了保证程序合理,要把constructor指向修改正确
        Cat.prototype.constructor = Cat; //修正constructor
        Cat.prototype.do = function () {
            console.log("玩红外线");
        }
        var cat1 = new Cat("white");
        console.log(cat1)
        console.log(cat1.say)
        console.log(cat1.constructor)
        console.log(Cat.prototype.constructor)
原型链+构造函数
	function Animal(name, age) {
            this.name = name;
            this.age = age;
        }
        Animal.prototype.say = function () {
            cosnole.log("吼~")
        }
        var ani = new Animal();
        ani.do = function () {

        }

        function Cat(color, name, age) {
            this.color = color;
            //构造函数继承
            Animal.call(this, name, age)
        }
        //原型链继承
        Cat.prototype = new Animal();
        Cat.prototype.constructor = Cat;
        Cat.prototype.do = function () {
            console.log("玩红外线");
        }
        var cat1 = new Cat("white", "wanan", "2");
        console.log(cat1)
        console.log(cat1.say)
        console.log(cat1.constructor)
        console.log(Cat.prototype.constructor)

多态

JS对象多态性是与生俱来的:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果,从而,给不同的对象发送同一个消息时,这些对象根据这个消息分别表示不同的反馈。

线程/事件机制

进程

  • 是程序的某一次执行,主要在内存开启一定的空间
    • 有的程序是单进程的,有的程序是多进程的(多个进程不可以共享数据)
    • 有的进程是单线程,有的进程是多线程(多个线程可以共享数据)

线程

  • 是进程的一个独立单元,是cpu的基本调度单位
    • 单线程
      • 编程简单
      • 效率低
    • 多线程
      • 可以提高cpu的利用率
      • 创建多个线程的时候会有额外开销
      • 切换线程的时候也会有额外的开销
      • 容易产生死锁(活锁/饿死)

定时器真的?

  • 定时器真的是定时执行的吗?

    • 并不能保证真正定时执行,一般会延迟一丁点(可以接受),也有可能延迟很持续
  • 定时器变量函数是在分线程执行的吗?

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

event loop

事件循环模型(事件引发机制)

事件轮询机制

代码分类

  • 同步代码
    • 初始化代码/绑定事件/定义计时器/发送ajax
  • 异步代码
    • 事件执行后的回调函数/计时器到期后的回调函数,ajax成功后的回调函数

js代码顺序

先执行同步代码

等待同步代码执行完成后才执行异步代码

事件循环模型两个重要内容

事件管理模块

回调队列

事件轮询机制

  1. 执行初始化同步代码,把相应的回调给到浏览器对应的管理模块(事件回调==> 事件管理模块、计时器回调==》计时器管理模块、ajax回调==》 ajax管理模块)
  2. 当事件发生或计时器到期等等,就会把相应的回调函数添加到回调队列中,等待执行
  3. 等初始化代码指向完毕,js引擎就会循环的检查回调队列中的回调函数,并执行

new操作符

  • new操作符做了什么事情
  1. 创建一个空对象o,将来会返回这个对象
  2. 调用构造函数,并将构造函数的this指向新创建的对象o
  3. 把原型对象的方法给新创建的对象,把新创建的对象的__proto__指向构造函数的显式原型
  4. 判断构造函数的返回值,来决定new的返回值(构造函数的返回值还是实例化对象)
  • 如果构造函数return一个对象,则实例化的返回值丢弃实例化对象,返回return的值
  • 如果构造函数return一个基本值,则忽略 ,仍然返回实例化对象

手写new

 function Car(color, speed) {
            this.color = color;
            this.speed = speed;
            return {
                name: "lily"
            }
        }

        Car.prototype.run = function () {
            console.log("驾~")
        }

        //封装一个手写new函数,当把构造函数传进去,就会模拟new 把它给实例化了
        function writeNew(Car) {
            //1
            var o = {};
            //2 arguments是实参,要的是取掉第一个实参的值
            //把arguments转成数组,并从下标为1开始切割  即可得到实例化对象的参数
            //result是调用car的返回值
            var result = Car.apply(o, Array.prototype.slice.call(arguments, 1));
            //3
            o.__proto__ = Car.prototype;
            //判断Car构造函数的返回值,如果是Object类型  则丢弃o 如果是基本类型 则返回o
            var isObj = typeof result === 'object' && result != null;
            var isFun = typeof result === 'function';

            return (isObj || isFun) ? result : o;
        }
        
        var car1 = writeNew(Car, "red", "200");
        var car2 = writeNew(Car, "green", "220");
        console.log(car1)
        console.log(car1 instanceof Car)
        console.log(car2 instanceof Car)
        console.log(car1.constructor)
        console.log(car2.constructor)
        console.log(car1.run === car2.run)


        //new:1.返回一个对象  2.对象上还有属性和方法
        var car3 = new Car("red", "200");
        var car4 = writeNew(Car, "green", "220")
        console.log(car3, car4)

函数

注意

如果变量声明和函数名相同,如果变量在没有赋值的情况下,不会影响函数执行

定义

  1. new Function
  2. function声明
  3. 函数表达式
var fn1 = new Function("a", "alert(a)");
        fn1(1);

        function fn2() {
            alert(2);
        }

        var fn3 = function () {

        }

调用

fn() //一般调用

new fn() //实例化调用

obj.fn() //对象上下文调用

fn.call() /fn.apply()/fn.bind()(); //call方法。。调用

传参

函数的传参操作其实是值的传递或者是引用值的传递而不是堆内数据的传递,

传参的传递只有值传递 而没有引用传递

o5传递给obj 只是把地址值传递给了obj 而不是把o5所代表的对象传递给obj

身份

  1. 作为函数 自身可以调用
  2. 作为一个对象,可以扩展属性和方法 所以函数也被称作为 函数对象
		function fn() {

        }
        fn();
        fn.study = "js";

回调函数

回调函数:(一定是一个函数)

  1. 你定义的
  2. 我们没有自主调用这个函数
  3. 函数最终执行了

常见的场景

​ - DOM事件

​ - 计时器

​ - ajax

​ - 生命周期函数

		div.onclick = function () {

        }

        [1, 2, 3].forEach(function () {

        })

        setTimeout(function () {

        }, 0)

IIFE

匿名函数执行 把匿名函数赋值给一个变量,变量调用

IIFE:匿名函数自调用

​ (匿名函数)()

​ +匿名函数()

IIFE的作用:

​ - 构建局部作用域(防止变量污染)

​ - 模块化

 		(function () {

        })();
        ! function () {

        }();


        for (var i = 0; i < 5; i++) {
            setTimeout(function () {
                console.log(i); //5 5 5 5 5
            })
        }

        for (var i = 0; i < 5; i++) {
            (function (i) {
                setTimeout(function () {
                    console.log(i); //5 5 5 5 5
                })
            })(i)
        }

this

this是函数中的一个变量,他指向的对象是根据函数调用时候决定的

  1. 自调用(默认绑定) 自调用的this指向window
  2. 对象调用(隐式绑定)
  3. 实例化调用,this指向实例化对象 指向f变量保存的对象
  4. 显示绑定(硬绑定)call apply bind
  5. 原型对象的this指向实例化对象

如何确定this的指向

​ - 首先确实是否是硬绑定,如果 是则this指向call所规定的对象

​ - 是否是实例化调用,如果是实例化调用,则this指向实例化对象

​ - 判断函数调用是否是被上下文对象调用的,如果是 则this指向上下文对象(要注意是否存在隐式丢失现象)

​ - 函数自调用 返回window

案例
 function fn1() {
            console.log(this);
        }
        //1.自调用(默认绑定)  自调用的this指向window
        fn1();
        [1, 2, 3].forEach(function (item, index) {
            console.log(this); //window 回调函数是自己调用的,数组调用的是forEach 而不是forEach中的回调
        })
        setTimeout(function () {
            console.log(this); //当事件到了之后调用,回调函数自己调用,指向window
        })



        // 2.对象调用(隐式绑定)
        var obj = {
            name: "lily",
            fn: fn1 //把fn1变量保存的引用地址 给了fn
        }
        obj.fn() //此时调用的fn1函数  函数调用有一个上下文对象obj  所以this指向的是obj

        document.onclick = fn; //事件函数this指向 绑定事件的元素


        //3.隐式丢失
        var obj1 = {
            name: "lily",
            fn: fn1 //把fn1变量保存的引用地址 给了fn
        }
        var fn2 = obj1.fn; //把obj1的fn属性保存的引用地址给了fn2
        fn2(); //window 这个属于默认绑定 自调用  和obj1没有任何关系


        //4.实例化调用
        var f = new fn1(); //this指向实例化对象 指向f变量保存的对象


        //5.显示绑定(硬绑定)call apply bind
        fn1.call(window) //window
        fn1.call(obj1) //obj1

call/apply/bind

作用

每一个函数都有这三个方法

这三个方法作用一致,都是改变函数运行时的上下文指向,其实就是改变函数中的this指向

不同

fn.call(fn的this指向,fn函数的参数 参数和参数之间逗号隔开)

fn.apply(fn的this指向,[数组,里边书写的是fn的参数])

fn.bind(fn的this指向,fn函数的参数 参数和参数之间逗号隔开) 和call类似

call\bind

bind 只是改变了this指向,并且返回了一个新函数的引用,并没有调用函数

prototype

被称作显示原型

  1. 每一个函数都有自己的显示原型(prototype属性)
  2. 函数只有在使用的时候如果是new调用,才是一个构造函数
  3. 显示原型(protoytpe其实是一个指针)指向的是当前函数的原型对象
  4. 所以每一个自己定义函数都有自己的原型对象 默认是空对象

constructor

每一个原型对象都有一个constructor属性,指向当前原型对象的构造函数

构造器
a.constructor  获取的是a的构造函数
'123'.constructor  --> f String
[].constructor  --> f Array

__proto__

每一个对象都有一个__proto__属性,我们把这个属性称作为隐式原型

在ES6之前,我们是不能操作隐式原型的

原型链总结

每一个函数都有一个显示原型 prototype

每一个对象都有一个隐式原型 __proto__

对象的隐式原型指向对象构造函数的显示原型

实例化的时候已经确定好了整个原型链

执行上下文

  1. js解析并不是真正的直接逐行解析,而是进入到一个新的作用域的时候,会有一个准备工作,这个准备工作被称为"执行上下文",执行上下文还在内存中为函数执行创建一个空间
  2. 新的作用域可以是全局作用域和局部作用域
  3. 全局的执行上下文只有一个,局部的执行上下文可以有多个
  4. 因为有多个执行上下文,所以js会创建一个执行上下文栈(stack),用来管理所有的执行上下文
  5. 代码最开始指向是先执行全局,所以会在stack中压入一个全局执行上下文,全局执行上下文等代码全部指向完毕才退出
  6. 当代码执行一个函数,此时就会创建一个新的局部执行上下文,并把它压入到stack中,当函数执行函数,就把当前执行上下文在stack中弹出
  7. 执行上下文包含3个内容:1.变量对象 2.确定this 3.激活作用域链

变量对象

  1. 变量对象是官方的专业名词,保存当前作用域所有的属性和方法
  2. 在执行上下文中,变量对象被创建和激活,只有变量对象被激活,我们才可以获取到当前作用域定义的属性和方法
  3. 变量对象分为全局变量对象和局部变量对象
  4. 全局的变量对象其实就是window对象,因为所有的全局变量和全局的函数都是window对象的属性和方法
  5. 局部变量对象
    • 创建局部执行上下文时候产生局部变量对象
    • 在变量对象中创建形参和实参
    • 把所有的函数声明放到变量对象中(函数优先提升),此时如果有重名的属性,则直接覆盖
    • 把当前作用域中所有的变量,以键值对的形式保存在变量对象中,此时所有的变量的值都应该是undefined
    • 当提升的变量如果和形参或函数有重名的,则不会干扰已经存在的这些属性(只有当执行到变量被赋值了,才会进行覆盖)

作用域链

  • 函数在创建的时候会创建一条作用域链(scope chain)
  • 作用域链:
    • 保证对 当前执行环境所能够访问的所有的变量和函数 有序访问
    • 作用域链实在声明函数的时候创建的
  • 作用域链访问所有的变量和函数
    • 也就是访问的是各个函数变量对象,其实真正意义上是一个各个作用域的变量对象组成的链条
  • 在函数定义的时候,创建的作用域链是不完整的的,因为当前的函数没有执行,所以作用域链的最前端没有变量对象
  • 当函数调用并创建执行上下文的时候,会把当前的作用域的变量对象放到作用域链的最前端,此时作用域链完整并激活
  • 作用域的最前端一定是当前的函数的变量对象,做末端一定是window对象
  • 当查找一个变量的时候,就会沿着作用域链去查找,如果差找不到就会抛出错误

闭包

闭包?

  1. 函数嵌套函数,包就是内部嵌套的函数
  2. 闭包是一个包含内部函数引入外部函数变量的那个对象

构成闭包的条件

  1. 函数嵌套
  2. 内部函数使用外部函数的变量
  3. 调用外部函数

你可能感兴趣的:(JavaScript)