javascript进阶面向对象ES6(ECMAScript 6.0) 学习笔记

ES6

Day01

1、面向对象编程介绍

1.1 两大编程思想

面向过程
面向对象

1.2 面向过程编程 POP

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。

面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。

1.3 面向对象编程 OOP

面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作;

面向对象是以对象功能来划分问题,而不是步骤。

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。

面向对象的特性:封装性、继承性、多态性;

1.4 面向过程和面向对象的对比

面向过程面向对象
优点:性能比面向对象高,适合跟硬件练习很紧密的东西,例如单片机就采用的面向过程编程;优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:没有面向对象易维护、易复用、易扩展优点:性能比面向过程低

2、ES6中的类和对象

面向对象
面向对象更贴近我们的实际生活,可以使用面向对象描述现实世界事物,但是事物分为具体的事物和抽象的事物。

面向对象的思维特点:
1、抽取(抽象)对象共有的属性和行为组织(封装)成一个类(模板);
2、对类进行实例化,获取类的对象;
面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情;

2.1 对象

在Javascript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数组、数值、函数等;

对象由属性和方法组成:
属性:事物的特征,在对象中用属性来表示(常用名词);
方法:事物的行为,在对象中用方法来表示(常用动词);

2.2 类 class

在ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象;

抽象了对象的公共部分,它泛指某一大类(class);
对象特指某一个,通过类实例化一个具体的对象;

2.3 创建类

语法:

class name {
  // class body
}

创建实例:

let xx = new name();

注意:类必须使用new实例化对象

2.4 创建实例

(1)通过class关键字创建类,类名习惯首字母大写;
(2)类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象;
(3)constructor 函数,只要new 生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数;
(4)生成实例new 不能省略;
(5)最后注意语法规范,创建类 类名后面不要加小括号,生成实例,类名后面加小括号,构造函数不需要加function;

2.5 类添加方法

语法:

class Star {
            constructor(name) {
                this.name = name;
            }
            sing() {
                console.log("我会唱歌")
            }
            dance() {
                console.log("我会跳舞");
            }
        }

(1)类里面所有的函数不需要写function;
(2)多个函数方法之间不需要添加逗号分隔;

3、类的继承

3.1 继承 extends

现实中的继承:子承父业,比如我们都继承了父亲的姓;
程序中的继承:子类可以继承父类的一些属性和方法;

语法:

class Father{ // 父类
}
class Son extends Father { //子类继承父类
}

3.2 super关键字

super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数;

constructor(props) {
              super(props);  //调用父类的构造函数
            }

super.say(); //调用父类的普通函数

3.3 super调用父类普通函数以及继承中属性方法查找原则(就近原则)

1、继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的方法;
2、继承中,如果子类里面没有,就去查找父类有么有这个方法,如果有,就执行父类的这个方法(就近原则);

3.4 super必须在子类this之前调用

3.5 三个注意点

1、在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象;
2、类里面的共有的属性和方法一定要加this使用;
3、类里面this的指向问题;
4、constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者;

4、面向对象案例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
案例总结:
1、在切换选项卡模块,用到了之前的排他思想;
2、this的指向问题很重要;
3、双击事件 :ondblclick
4、that.lis[index].click()可以自动触发点击事件,不需要鼠标再去点击
5、input.select(); 让文本框里面的文字处于选定状态
6、this.blur(); 自动触发失去焦点事件
7、禁止双击选中文字

window.getSelection?window.getSelection().removeAllRanges():document.selection.empty();

8、逻辑判断

// 当that.lis[index]为真,才会执行that.lis[index].click()
            that.lis[index] && that.lis[index].click();

9、阻止冒泡

 //阻止冒泡,点击关闭按钮,因为冒泡,也会触发父级元素的点击事件
            e.stopPropagation();

10、添加元素

 that.ul.insertAdjacentHTML("beforeend", li);

insertAdjacentHTML(“beforeend”, li);这个函数可以把字符串直接添加到HTML中,第一个参数意思是把要添加的li添加到后面;

11、this.lis[i].onclick = this.toggleTab; // 后面不能加括号,否则页面加载就会调用


Day02

1、构造函数和原型

在ES6之前,我们想使用面向对象,就使用构造函数创建面向对象;

1.1 概述

在典型的OOP的语言中(如java),都存在类的概念,类就是对象的模板,对象就是类的实例,
但在ES6之前,JS中并没有引入类的概念;

ES6,全称ECMAScript 6.0 ,2015.06发布。但是目前浏览器的Javascript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能;

在ES6之前,对象不是基于类创建的,而是用一种称为构造函数的特殊函数来定义对象和它们的特征;

创建对象可以通过以下三种方式:
1、对象字面量;
2、new Object();
3、自定义构造函数

1.2 构造函数

构造函数一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

(1)在JS中,使用构造函数时要注意以下两点:

1、构造函数用于创建某一类对象,其首字母要大写
2、构造函数要和new一起使用才有意义;

(2)new 在执行时会做四件事情:

(1)在内存中创建一个新的空对象;
(2)让this指向这个新的对象;
(3)执行构造函数里面的代码,给这个新对象添加属性和方法;
(4)返回这个新对象(所以这个构造函数里面不需要return);

(3)实例成员和静态成员

javascript的构造函数之后可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。通过这两种方式添加的成员,就分别称为静态成员实例成员

静态成员:在构造函数本身添加的成员称为静态成员,只能由构造函数本身来访问
实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问

<script>
    // 利用构造函数创建对象
    // 构造函数中的属性和方法我们称为成员,成员可以添加
    // 1、实例成员 就是构造函数内部通过this添加的成员 name age 就是实例成员
    function Star(name,age){
        this.name = name;
        this.age = age;
    }

    // 创建实例
    const zhangsan = new Star("zhangsan",19);
    console.log(zhangsan);
    
    // 2、静态成员 在构造函数本身上添加的成员 sex就是静态成员
    Star.sex = "男"; // 不能通过对象来访问
    console.log(Star.sex);  
</script>

1.3 构造函数的问题

构造函数方法很好用,但是存在浪费内存的问题。每次创建实例,都会开辟单独的空间存放同一个函数,浪费内存。

我们希望所有的对象使用同一个函数,这样就比较节省内存,那么怎么做呢?

1.4 构造函数原型prototype

构造函数通过原型分配的函数是所有对象共享的

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有;

我们可以把那么不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。

原型是什么:一个对象,我们也称为prototype为原型对象;
原型的作用是什么共享方法;

1.5 对象原型__proto__

对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。

(1)__proto__对象原型和原型对象prototype是等价的;
(2)__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype;

方法的查找规则:
(1)首先看实例对象身上是否有sing方法,如果有执行这个对象上的sing
(2)如果没有sing这个方法,因为有__proto__的存在,就去构造函数原型对象prototype身上去查找sing这个方法;

1.6 constructor 构造函数

对象原型(proto)和构造函数原型对象(prototype)里面都有一个constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。

很多情况下,我们需要手动的利用constructor这个属性指回 原来的构造函数;
如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数;
在这里插入图片描述

1.7 构造函数、实例、原型对象三者之间的关系

在这里插入图片描述

1.8 原型链

在这里插入图片描述

1.9 Javascript 的成员(属性或方法)查找机制(规则)

1、当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性;
2、如果没有就查找它的原型(也就是__proto__指向的prototype原型对象);
3、如果还没有就查找原型对象的原型(Object的原型对象);
4、以此类推一直找到Object为止(null);

1.10 原型对象this指向

1、在构造函数中,里面this指向的是对象实例 zhangsan
2、原型对象函数里面的this,指向的是实例对象 zhangsan

<script>
    function Star(name, age) {
        this.name = name;
        this.age = age;
    }
    var that;
    Star.prototype = {
        constructor: Star,
        sing: function () {
            console.log("I can sing");
            that = this;
        },
        movie: function () {
            console.log("I can dance");
        },
    }
    // 1、在构造函数中,里面this指向的是对象实例 zhangsan
    const zhangsan = new Star("zhangsan", 19);
    // 2、原型对象函数里面的this,指向的是实例对象 zhangsan
    zhangsan.sing();
    console.log(that);
</script>

1.11 利用原型对象扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能;

注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式;


2、继承

ES6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承

2.1 call 方法的作用

1、可以调用函数;
2、可以改变这个函数的this指向;

fun.call(thisArg, arg1,agr2);

thisArg:当前调用函数this的指向对象;
arg1/arg2:传递的其他参数
<script>
        function fn(){
            console.log("111");
            console.log(this);
        }

        let o = {
            name:"andy"
        };
        // 1、调用函数
        fn.call();
        // 2、改变函数的this指向
        // 此时这个函数的this指向的是o这个对象
        fn.call(o , 1 , 2);
    </script>

2.2 借用构造函数继承父类型属性

核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性;

 <script>
    // 1、父构造函数
    function Father(uname,age){
        this.uname = uname;
        this.age = age;
    }

    // 2、子构造函数
    function Son(uname,age){
        // this指向子构造函数的对象实例
        Father.call(this, uname, age);
    }

    const son = new Son('zhangsan',18);
    console.log(son);
    </script>

2.3 利用原型对象继承方法

在这里插入图片描述

<script>
        // 1、父构造函数
        function Father(uname, age) {
            this.uname = uname;
            this.age = age;
        }

        Father.prototype.money = function () {
            console.log(100000);
        }

        Son.prototype = new Father();

        console.log(Son.prototype.__proto__);
        // 2、子构造函数
        function Son(uname, age) {
            // this指向子构造函数的对象实例
            Father.call(this, uname, age);
        }

        Son.prototype.exam = function () {
            console.log("孩子要考试");
        }

        const son = new Son('zhangsan', 18);
        son.money()
    </script>

在这里插入图片描述

2.4、ES6 类的本质

ES6之前通过 构造函数 + 原型 实现面向对象编程;
(1)构造函数有原型对象prototype;
(2)构造函数原型对象prototype里面有constructor指向构造函数本身;
(3)构造函数可以通过原型对象添加方法;
(4)构造函数创建的实例对象有__proto__原型指向构造函数的原型对象;

ES6 通过类 实现面向对象编程;

类的本质其实就是一个函数,我们也可以简单的认为,类就是构造函数的另一种写法;

1、class本质还是function
2、类的所有方法都定义在类的prototype属性上;
3、类创建的实例,里面也有__proto__指向类的prototype原型对象;
4、所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已;
5、所以ES6的类其实就是语法糖;
6、语法糖:语法糖就是一种便捷写法。简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖;

3、ES5中新增的方法

3.1 ES5 新增方法概述

ES5中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:
(1)数组方法
(2)字符串方法
(3)对象方法

3.2 数组方法

迭代(遍历)方法:forEach()、map()、filter()、some()、every();

3.2.1 迭代(遍历数组)forEach方法
array.forEach(function(currentValue,index,arr))

currentValue:数组当前项的值;
index:数组当前项的索引;
arr:数组对象本身;

<script>
        var arr = [1,2,3];
        let sum = 0;
        arr.forEach(function(value,index,arr){
            console.log(value);
            sum += value;
        })
        console.log(sum);
    </script>
3.2.2 筛选数组 filter 方法
array.filter(function(currentValue,index,arr))

filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组;

注意它直接返回一个新数组;

currentValue:数组当前项的值;
index:数组当前项的索引;
arr:数组对象本身;

3.2.3 查找数组中是否有满足条件的元素some方法
array.some (function(currentValue,index,arr))

currentValue:数组当前项的值;
index:数组当前项的索引;
arr:数组对象本身;

some()方法用于检测数组中的元素是否满足指定条件。通俗点,查找数组中是否有满足条件的元素;

注意:它返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false;如果 查询数组中唯一的元素,some效率更高

如果找到第一个满足条件的元素,则终止循环,不再继续查找;

3.3 字符串方法

3.3.1 trim方法去除字符串两侧空格
<script>
        var str = '   andy';
        // 去除左右两侧的空格
        str = str.trim();
        console.log(str);
        console.log(str.length);
    </script>

3.4 对象方法

3.4.1 Object.keys 方法获取对象属性名

用于获取对象自身所有的属性;

 Object.keys(obj);

效果类似于for…in…
返回一个由属性名组成的数组;

<script>
        let obj = {
            id:1,
            name:"小牧"
        }

        let keys = Object.keys(obj);
        console.log(keys);
    </script>
3.4.2 Object.defineProperty()定义对象中新属性或修改原有的属性
Object.defineProperty(obj,prop,descriptor);

参数说明:
obj:必需。目标对象;
prop:必需。需定义或修改的属性的名字;
descriptor:必需。目标属性所拥有的特性;

第三个参数进一步说明:以对象形式书写{}书写
value:设置属性的值 默认为undefined;
writeable:值是否可以重写。true|false 默认为false;
enumerable:目标属性是否可以被枚举。true|false 默认为false;
configurable:目标属性是否可以被删除或是否可以再次修改特性 true|false 默认为false;

<script>
        let obj = {
            id:1,
            name:"小米",
            price:"1000"
        }

        Object.defineProperty(obj,'num',{
            value:1000,
            writable:false,
            enumerable:false,
            configurable:false
        })

        console.log(obj); // {id: 1, name: "小米", price: "1000", num: 1000}
        obj.num = 2000;  
        console.log(obj); // {id: 1, name: "小米", price: "1000", num: 1000}
    </script>

Day03

1、函数的定义和调用方式

1.1 函数的定义方式

1、函数声明方式function关键字(命名函数);
2、函数表达式(匿名函数);
3、new Function()

var fn = new Function('参数1','参数2',...,'函数体');

Function里面参数都必须是字符串格式;
第三种方式执行效率低,也不方便书写,因此很少使用;
所有函数都是function的实例(对象);
函数也属于对象;

在这里插入图片描述

1.2 函数的调用方式

6种函数的调用方式:
1、普通函数
2、对象的方法
3、构造函数
4、绑定事件的函数
5、定时器函数
6、立即执行函数 立即执行函数是自动调用的

<script>
        // 函数的调用方式
        // 1、普通函数
        function fn(){
            console.log("普通函数");
        }
        fn(); 

        // 2、对象的方法
        var o = {
            sayHi:function(){
                console.log("对象的方法");
            }
        }
        o.sayHi();

        // 3、构造函数
        function Star(){}
        new Star();

        // 4、绑定事件的函数
        btn.onclick = function(){}

        // 5、定时器函数
        setInterval(function(){},1000);
        
        // 6、立即执行函数  立即执行函数是自动调用的
        (function(){
            console.log("立即执行函数"); 
        })
    </script>

2、this

2.1 函数内部的this指向

这些this的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this的指向不同;
一般指向我们的调用者。

调用方式this指向
普通函数调用window
构造函数调用实例对象 原型对象里面的方法也指向实例对象
对象方法调用该方法所属对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window

2.2 改变函数内部this指向

Javascript 为我们专门提供了一些函数方法来帮助我们更优雅的处理函数内部this的指向问题,常用的有bind()、call()、apply()三种方法。

1、call方法

call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的this指向;

fun.call(thisArg,arg1,agr2,...)

主要应用:实现继承

2、apply方法

apply()方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的this指向。

fun.apply(thisArg, [argsArray]);

thisArg:在fun函数运行时指定的this值;
argsArray:传递的值,必须包含在数组里面;
返回值就是函数的返回值,因为它就是调用函数;

apply的主要应用 比如说我们可以利用apply 借助于数学内置对象求最大值

3、bind方法

bind()方法不会调用函数。但是能改变函数内部this指向;

fun.bind(thisArg, arg1,arg2,...);

thisArg:在fun函数运行时指定的this值;
arg1,arg2:传递的其他参数;
返回由指定的this值和初始化参数改造的原函数拷贝

主要应用:如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用bind;
情境:我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮

3、严格模式

3.1 什么是严格模式?

Javascript除了提供正常模式外,还提供了严格模式(strict mode)。ES5的严格模式是采用具有限制性Javascript变体的一种方式。即在严格条件下运行JS代码;

严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略;
严格模式对正常的Javascript语义做了一些更改:
1、消除了Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为;
2、消除代码运行的一些不安全之处,保证代码运行的安全;
3、提高编译器效率,增加运行速度;
4、禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫。比如一些保留字如:class、enum、export、extends、import、super不能做变量名;

3.2 开启严格模式

严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为脚本开启严格模式和为函数开启严格模式两种情况。

3.2.1 为脚本开启严格模式

有的script基于是严格模式,有的script脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中,这样独立创建一个作用域而不影响其他script脚本;

<script>
  (function(){
      'use strict';
      var num = 0;
      function fn(){}
})();
</script>
3.2.2 为函数开启严格模式

要给某个函数开启严格模式,需要把“use strict”;(或‘use strict’;)声明放在函数体所有语句之前;

3.3 严格模式中的变化

严格模式对Javascript的语法和行为,都做了一些改变;

3.3.1 变量规定

1、在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var命令声明,然后再使用;
2、我们不能随意删除已经声明好的变量;

3.3.2 严格模式下this指向问题

1、以前在全局作用域函数中的this指向window对象;
2、严格模式下全局作用域中函数中的this是undefined;
3、以前构造函数时不加new也可以调用,当普通函数,this指向全局对象;
4、严格模式下,如果构造函数不加new调用,this会报错;
5、new实例化的构造函数指向创建的对象实例;
6、严格模式下定时器this还是指向window;
7、事件、对象还是指向调用者;

3.3.3 函数变化

1、严格模式下函数里面的参数不允许有重名;
2、函数必须声明在顶层。新版本的Javascript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。

4、高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数将函数作为返回值输出

函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。

5、闭包

5.1 变量作用域

变量根据作用域的不同分为两种:全局变量和局部变量;
1、函数内部可以使用全局变量;
2、函数外部不可以使用局部变量;
3、当函数执行完毕,本作用域内的局部变量会销毁;

5.2 什么是闭包

闭包(closure)指有权访问另一个函数作用域中变量的函数
简单理解就是,一个作用域可以访问另外一个函数内部的局部变量;

被访问变量所在的函数就是闭包函数

5.2.1 闭包的作用

闭包的主要作用:延伸了变量的作用范围;

5.2.2 闭包总结

1、闭包是什么?
闭包是一个函数(一个作用域可以访问另一个函数的局部变量);

2、闭包的作用是什么?
延伸变量的作用范围;

6、递归

6.1 什么是递归?

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己,这个函数就是递归函数。

递归函数的作用和循环效果一样。

由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return;

6.2 利用递归求斐波那契数列

<script>
    function fn(n) {
        if(n == 1 || n==2) {
            return 1;
        }
        return fn(n-1) + fn(n-2);

        // fn(3-1) + fn(3-2) =>fn(2)+fn(1)
        // 1+1
    }
    console.log(fn(6));
    </script>

6.3 利用递归求:根据id返回对象的数据对象

 <script>
        let data = [
            {
                id: 1,
                name: "家电",
                goods: [{
                    id: 11,
                    gname: '冰箱'
                }, {
                    id: 22,
                    gname: "洗衣机"
                }]
            },
            {
                id:2,
                name:"服饰"
            }
        ]

        // 我们想要做输入id号,就可以返回数据对象
        function fn(data, id) {
            let o = {};
            data.forEach(function(item){
                if(item.id == id) {
                    o = item;
                } else if (item.goods && item.goods.length>0) {
                    o = fn(item.goods, id);
                }
            })

            return o;
        }
        console.log( fn(data, 11));
    </script>

6.4 浅拷贝和深拷贝

1、浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用,修改值,被拷贝的值也会发生变化;
2、深拷贝拷贝多层,每一级别的数据都会拷贝;
3、Object.assign(target,sourceObj); ES6新增方法;

深拷贝:

 <script>
        var obj = {
            name:"andy",
            goods:[
                {name:"nihao"}
            ]
        };
        var o = {};
        function deepCopy(o, obj) {
            for(var k in obj) {
                // 判断我们的属性值属于哪种数据类型
                var item = obj[k];
                // 判断这个值是否是数组
                if(item instanceof Array) {
                    o[k] = [];
                    deepCopy(o[k],item);
                } else if (item instanceof Object) {
                     // 判断这个值是否是对象
                    o[k] = {};
                    deepCopy(o[k], item);
                } else {
                    // 属于简单数据类型
                    o[k] = item;
                }
            }
        }
        deepCopy(o, obj)
        console.log(o);
    </script>

Day04

1、正则表达式概述

正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在Javascript中,正则表达式也是对象。

正则表达式通常被用来检索、替换哪些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线,昵称输入框可以输入中文(匹配)。此外,正则表达式还常用于过滤页面内容中的一些敏感词,或从字符串中获取我们想要的特定部分(提取)等。

2、正则表达式的特点

1、灵活性、逻辑性和功能性非常的强;
2、可以迅速地用极简单的方式达到字符串的复杂控制;
3、对于刚接触的人来说,比较晦涩难懂。比如:^\w+;
4、实际开发中,一般都是直接复制写好的正则表达式,但是要求会使用正则表达式并且根据实际情况修改正则表达式。比如用户名:/^[a-z0-9]{3-16}$/

3、正则表达式在Javascript中的使用

3.1 创建正则表达式

在Javascript中,可以通过两种方式创建一个正则表达式;

1、通过调用RegExp对象的构造函数创建;

var 变量名 = new RegExp(/表达式/);

2、利用字面量创建 正则表达式

var rg = /123/;

3.2 测试正则表达式 test

test()正则对象方法,用于检测字符串是否符合该规则,该对象会返回true或false,其参数是测试字符串;

regexObj.test(str);

1、regexObj 是写的正则表达式;
2、str 我们要测试的文本;
3、就是检测str文本是否符合我们写的正则表达式规范;

4、正则表达式中的特殊字符

4.1 正则表达式的组成

一个正则表达式可以由简单的字符构成,比如/abc/,也可以是简单和特殊字符的组合,比如/ab*c/.其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如^、$、+等。

特殊字符非常多,可以参考:
MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
正则测试工具:http://tool.oschina.net/regex

4.2 边界符

正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符:

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)

如果^ 和 $ 在一起,表示必须是精确匹配

4.3 字符类

字符类:[]表示有一系列字符可供选择,只要匹配其中一个就可以了;

 <script>
        var rg = /[abc]/; // 只要包含有a或者包含b或者c,都返回true
        
        var rg1 = /^[abc]$/;// 三选一,只有是a或者b或者c,这三个字母才返回true

        var rg2 = /^[a-z]$/;  // 26个英文字母任何一个字母返回true

        // 字符组合
        var rg3 = /^[a-zA-Z0-9_-]$/;  // 多选一

        // 如果[]里面有^,表示取反的意思,千万和我们边界符^别混淆
        var rg4 = /^[^a-zA-Z0-9_-]$/;  // 
    </script>

4.4 量词符

量词符:用来设定某个模式出现的次数;

量词说明
*重复零次或更多次
+重复一次或更多次
重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n次到m次(逗号之间不能有空格)

4.5 括号总结

(1)中括号[] 字符集合,匹配方括号中的任意字符;
(2)大括号{} 量词符,里面表示重复次数
(3)小括号() 表示优先级

可以在线测试:https://c.runboob.com

4.6 预定义类

预定义类指的是某些常见模式的简写方式;

预定类说明
\d匹配0-9之间的任一数字,相当于[0-9]
\D匹配所有0-9以外的字符,相当于[^0-9]
\w匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
\W除所有字母、数字和下划线,相当于[^A-Za-z0-9_]
\s匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f]
\S匹配非空格,相当于[^\t\r\n\v\f]

正则表达式里面的或是一条竖线:“|”;

5、正则表达式中的替换

5.1 replace 替换

replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式;

String.replace(str1,str2);

第一参数:被替换的字符串或者正则表达式;
第二个参数:替换为的字符串;
返回值是一个替换完毕的新字符串;

5.2 正则表达式参数

/表达式/[switch]

switch(也称为修饰符)按照什么样的模式来匹配,有三种值:
g:全局匹配;
i:忽略大小写;
gi:全局匹配+忽略大小写;

Day06

1、什么是ES6

ES6实际上是一个泛指,泛指ES2015及后续的版本。

2、let关键字

ES6中新增的用于声明变量的关键字。
(1)使用let关键字声明的变量具有块级作用域,防止循环变量变成全局变量;;
(2)不存在变量提升;
(3)暂时性死区;

var num =10;
if(true) {
 console.log(num);  // num is undefined
 let num = 20;
}

注意:在一个大括号中,使用let关键字声明的变量才具有块级作用域,var关键字不具备这个特点的;

3、const关键字

作用:声明常量,常量就是值(内存地址)不能变化的量;

(1)具有块级作用域;
(2)声明常量时必须赋值;
(3)常量赋值后,值不能修改;

4、 let const var 的区别

varletconst
函数级作用域块级作用域块级作用域
变量提升不存在变量提升不存在变量提升
值可更改值可更改值不可更改

5、解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

5.1 数组解构

在这里插入图片描述

5.2 对象解构

在这里插入图片描述

6、箭头函数

ES6中新增的定义函数的方式。

()=>{}

const fn = ()=>{}

(1)函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号;
(2)如果形参只有一个,可以省略小括号;

6.1 箭头函数中的this关键字

箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this;

<script>
        var obj = {
            age:20,
            say:()=>{
                // 对象不能产生作用域,相当于say方法是被定义在全局作用域中的
                alert(this.age);  // undefined
            }
        };

        obj.say();
    </script>

7、剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组;

7.1 剩余参数和解构配合使用

<script>
        let arr1 = ['zhangsan','lisi','wangwu'];
        let [s1,...s2] = arr1;
        console.log(s1); // zhangsan
        console.log(s2); // ['lisi','wangwu']
</script>

8、ES6的内置对象扩展

8.1 Array的扩展方法 扩展运算符(展开方法)

扩展运算符可以将数组或者对象转为用逗号分隔的参数序列;

 <script>
        let ary = ['a','b','c'];
        console.log(...ary);  // a b c
        console.log("a","b","c");
 </script>

扩展运算符可以应用于合并数组。
扩展运算符可以将类数组或可遍历对象转换为真正的数组

 <script>
        // let ary = ['a','b','c'];
        // console.log(...ary);  // a b c
        // console.log("a","b","c");

        // 扩展运算符应用1、合并数组
        let ary1 = [1,2,3];
        let ary2 = [4,5,6];
        // 方法1
        // let ary3 = [...ary1,...ary2];
         // 方法2
        ary1.push(...ary2);
        console.log(ary1);
       
        // 扩展运算符应用2、将类数组或可遍历对象转换为真正的数组
        let oDivs = document.querySelectorAll("div");
        var arr = [...oDivs];
        arr.push("a");
        console.log(arr);

    </script>

8.2 Array的扩展方法 构造函数方法 Array.from()

将类数组或可遍历对象转换为真正的数组;
方法还接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组;

<script>
        var arraylike = {
            "0":"1",
            "1":"2",
            "length":2
        }

        var arr = Array.from(arraylike, item=>item*2);
        console.log(arr);  // [2,4]
    </script>

8.3 Array的扩展方法 实例方法find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined;

<script>
        let ary = [
            {
                id:1,
                name:"zhangsan"
            },
            {
                id:2,
                name:"lisi"
            },
            {
                id:2,
                name:"wangwu"
            }
        ];
        let target = ary.find(item => item.id==2);
        console.log(target);  // {id:2,name:"lisi"}
</script>

8.4 Array的扩展方法 实例方法findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到就返回-1;

 <script>
        let arr = [10,20,30];
        let index = arr.findIndex(item=>item == 10);
        console.log(index);  // 1
    </script>

8.5 Array的扩展方法 实例方法includes()

表示某个数组是否包含给定的值,返回布尔值;

 <script>
        let arr = [10,20,30];
        console.log(arr.includes(20));  // true
        console.log(arr.includes(40));  // false
    </script>

8.6 String的扩展方法 模板字符串

模板字符串
ES6新增的创建字符串的方式,使用反引号定义。
模板字符串可以解析变量;
模板字符串中可以换行;
模板字符串中可以调用函数;

<script>
        // 模板字符串可以解析变量
        let name = "zhangsan";
        console.log(`我的名字是${name}`); // 我的名字是zhangsan

        // 模板字符串可以调用函数
        const fn = ()=>{
            return "我是fn函数"
        };
        console.log(`我要调用 ${fn()}`);  // 我要调用 我是fn函数

        // 模板字符串可以换行
        let html = `
        1
        2
        `;
        console.log(html);
    </script>

8.7 String的扩展方法 实例方法 startsWith()和endsWith()

**startsWith():**表示参数字符串是否在原字符串的头部,返回布尔值;
**endsWith()😗*表示参数字符串是否在原字符串的尾部,返回布尔值;

<script>
        let str = "Hello world!";
        console.log(str.startsWith("Hello")); // true
        console.log(str.endsWith("!")); // true
</script>

8.8 String的扩展方法 实例方法 repeat()

repeat方法表示将原字符串重复n次,返回一个新字符串;

<script>
        let str = "y".repeat(5);
        console.log(str); // yyyyy
</script>

9、Set数据结构

ES6提供了新的数据结构set。它类似于数组,但是成员的值都是唯一的,没有重复的值
利用Set数据结构做数组去重;

Set本身是一个构造函数,用来生成Set数据结构。

const s = new Set();

Set 函数可以接受一个数组作为参数,用来初始化;

const set = new Set([1,2,3,4,5]);
<script>
        const set = new Set([1,1,2,2]);
        console.log(set.size); //2
        const ary = [...set]; // 转换为数组
        console.log(ary);  // [1,2]
</script>

9.1 Set数据结构 实例方法

add(value) :添加某个值,返回set结构本身;
delete(value):删除某个值,返回一个布尔值,表示删除是否成功;
has(value):返回一个布尔值,表示该值是否为Set的成员;
clear():清除所有成员,没有返回值;

9.2 Set数据结构 遍历

Set 结构的实例和数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值;

s.forEach(value => console.log(value));

JS面试真题

第二章 变量类型和计算

  • 值类型 VS 引用类型,堆栈模型,深拷贝;
  • typeof操作符
  • 类型转换,truly和false变量;
1、typeof能判断哪些类型?

识别所有值类型;识别函数;判断是否是引用类型(不可再细分);

2、何时使用 === 何时使用 ==

除了 == null之外,其他都一律用 ===

补充:(1)字符串拼接 (2)== (3)if语句和逻辑运算;

这三种情况会发生隐式类型转换;

3、值类型和引用类型的区别
4、手写深拷贝

(1)注意判断值类型和引用类型;

(2)注意判断是数组还是对象;

(3)递归

第三章 原型和原型链

1、如何准确判断一个变量是不是数组?

2、手写一个简易的jQuery,考虑插件和扩展性

3、class的原型本质,怎么理解?

知识点

  • class和继承
  • 类型判断instanceof
  • 原型和原型链

class:

constructor

属性

方法

继承 extends super

第四章 作用域和闭包

1、this 的不同应用场景,如何取值?

2、手写bind函数

3、实际开发中闭包的应用场景,举例说明

闭包隐藏数据,只提供API,如做一个简单的cache工具;

知识点

  • 作用域和自由变量
  • 闭包
  • this

作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

自由变量

  • 一个变量在当前作用域没有定义,但被使用了;
  • 向上级作用域,一层一层依次寻找,直至找到为止;
  • 如果到全局作用域都没找到,则报错xx is not defined;

闭包

闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的;

闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!

this

注意:this取什么值,是在函数执行的时候确定的,不是在函数定义的时候确定的;

  • 作为普通函数
  • 使用call apply bind
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数

第五章 异步和单线程

1、同步和异步的区别是什么?

2、手写用Promise加载一张图片

3、前端使用异步的场景有哪些

知识点:

  • 单线程和异步

  • 应用场景

  • callback hell 和Promise

Promise

按照用途来解释:

  • 主要用于异步计算;
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果;
  • 可以在对象之间传递和操作Promise,帮助我们处理队列;

回调有四个问题

  • 嵌套层次很深,难以维护;
  • 无法正常使用return和throw;
  • 无法正常检索堆栈信息;
  • 多个回调之间难以建立联系;

Promise.all()

1、Doctype作用?标准模式与混杂模式有什么区别?

混杂模式让IE像IE5一样,支持一些非标准的特性,标准模式让IE具有兼容标准的行为。虽然这两种模式的主要区别只体现在通过CSS渲染的内容方面。但对JS也有一些关联影响,或称为副作用。第三种文档模式:准标准模式,这种模式下的浏览器支持很多标准的特性,但是没有标准规定的那么严格。主要区别在于如何对待图片元素周围的空白(在表格中使用图片时最明显)。准标准模式与标准模式非常接近,很少需要区分。

2、script的async跟defer的区别?

可以使用defer属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。

可以使用async属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中的出现的次序执行。

3、meta元素都有什么?

Metadata ——元数据,简单的来说就是描述数据的数据。

meta元素包含四大属性:charset、http-equiv、content、name;

charset声明了页面的字符编码;

content:此属性包含http-equiv或name属性的值,具体取决于所使用的值;

http-equiv属性定义了一个编译指示指令,所有允许的值都是特定HTTP头部的名称,通过定义该属性可以改变服务器和用户代理的行为。

name和content属性可以一起使用,以名-值对的方式给文档提供元数据,其中name作为元数据的名称,content作为元数据的值。

4、页面导入样式时,使用link和@import有什么区别?

(1)link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS;

(2)页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;

(3)import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题;

5、介绍一下你对浏览器内核的理解?

浏览器内核,又叫渲染引擎或排版引擎,渲染HTML、css等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值