ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

js高阶

2021-10-10 19:00:08  阅读:246  来源: 互联网

标签:function console 函数 对象 js var 高阶 log


1. 面向对象编程介绍

1.1 两大编程思想

--- 面向过程

--- 面向对象

1.2 面向过程编程 POP

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

举个例子:将大象装进冰箱-------面向过程做法

1、打开冰箱门

2、把大象装进去

3、关上冰箱门

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

1.3 面向对象编程 OOP

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

举个例子:将大象装进冰箱-------面向对象做法

先找出对象。并写出对象的功能:

1、大象对象---进去

2、冰箱对象

​ a.打开

​ b.关闭

3、使用大象和冰箱的功能

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

特性:封装性、继承性、多态性

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

2. ES6中的类和对象

面向对象的思维特点

1、抽取对象公用的属性和行为组织(封装)成一个类(模板)

2、对象进行实例化,获取类的对象

面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情。

2.1 对象

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

2.2 类 class

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

​ ---类 抽象了对象的公共部分,它泛指某一个大类

​ ---对象 特指某一个,通过实例化一个具体的对象

2.3 创建类

class phone { // 创建一个手机类
  //class body
}
// 利用类创建对象
new phone();

2.4 类 constructor 构造函数

constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor() 。

class Star {
  // 类的共有属性放到 constructor 里面
        constructor(uname, age) {
            this.uname = uname;
            this.age = age;
        }
    }

		// 利用类创建对象 new
    var ty = new Star("唐嫣", 18);
    console.log(ty);
		console.log(ty.uname);

(1) 通过class 关键字创建类,类名我们还是习惯性定义首字母大写

(2) 类里面通过constructor 函数,可以接收传递过来的参数,同时返回实例对象

(3) constructor 函数 只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,对象也会自动生成这个函数

(4) 生成实例 new 不能省略

(5) 最后注意语法规范,创建类 类名后面不要加小括号,生成实例 累了吗后面加小括号,构造函数不需要加function

2.5 类创建方法

        class Star {
            // 类的共有属性放到 constructor 里面
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            say(language) {
                console.log(this.uname + language);
                // console.log(this.uname + "你好");
            }
        }
        // 利用类创建对象 new
        var ty = new Star("唐嫣", 18);
        console.log(ty);
        console.log(ty.uname);
        // (1) 类里面的所有函数不需要写function
        // (2) 多个函数方法之间不需要用逗号间隔
        ty.say("我是恁爹");

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

3. 类的继承

3.1 继承

现实中的继承:子承父业,继承父亲的姓

程序中的继承:子类可以继承父类的一些属性和方法

3.2 super 关键字

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

继承中的属性或者方法查找原则:就近原则

1.继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的

2.继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

3.3 注意事项

  1. 在es6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
  2. 类里面的共有的属性和方法一定要加this 使用

4. 构造函数和原型

4.1 概述

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

4.2 构造函数

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

new 在执行时会做四件事情

---在内存中创建一个新的空对象

---让this 指向这个新的对象

---执行构造函数里面的代码,给这个新对象添加属性和方法

---返回这个新对象(所以构造函数里面不需要return)

4.3 构造函数的问题

构造函数方法很好用,但是存在浪费内存的问题.

创建了两个实例化对象,两个对象中都有sing 方法,sing方法又会在内存中新开辟一个来存放函数,两个对象的sing 方法都会占用内存空间用来存放同一个函数,就会比较浪费内存.

​两个对象的sing方法不一样.

4.4 构造函数原型 prototype

prototype 是一个对象,里面存放的是constructor 函数,又叫做原型对象

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

4.5 对象原型 __proto__

对象都会有一个属性 __proto__指向构造函数的prototype 原型对象,之所以我们对象可以使用构造函数prototype 原型对象的属性和帆帆发,就是因为对象有 ____proto__原型的存在.proto两边都有两个下划线(__)

4.6 constructor 构造函数

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

4.8 原型链

一层一层的往上找

4.9 JavaScript 的成员查找机制(规则)

① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性

② 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)

③ 如果还没有就查找原型对象的原型(Object的原型对象)

④ 依此类推一直找到Object为止(null)

⑤ __proto__对象原型的意义就在于为对象成员查找机制提一个方向,或者说时一条路线

4.10 原型对象中 this 指向问题

function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function () {
            console.log('我会唱歌');
            that = this;
        }
        var ldh = new Star('刘德华', 18);
        // 1. 在构造函数中,里面this 指向的是对象实例 ldh 
        ldh.sing();
        console.log(that === ldh);

        // 2. 原型对象函数里面的this 指向的是 实例对象 ldh 

4.11 扩展内置对象

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

Array.prototype.sum = function () {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        }
        var arr = [1, 2, 3, 4, 5];
        console.log(arr.sum());
        console.log(Array.prototype);

注意;数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式。前者会把其他方法覆盖掉、后者是在原有的基础上追加一个函数。

5. 继承

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

5.1 call()函数

调用这个函数,并且修改函数运行时的this 指向

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

thisArg:当前调用函数this的指向对象

arg1,arg2:传递的其他参数

// call 方法
        function fn(x, y) {
            console.log(this);
            console.log(x + y);
        };
        var obj = {
            name: 'john'
        };
        // fn();
        // 1. call() 可以调用其他函数

        // fn.call(obj);
        // 2. call() 可以改变这个函数的this 指向,此时这个函数的的this 就指向了这个obj 对象

        fn.call(obj, 1, 2);
        // 3. 函数指向了obj 这个对象,后面的值参与了运算

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

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

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

        Father.prototype.money = function () {
            console.log('挣钱了');
        }

        // 2. 子构造函数
        function Son(uname, age, score) {
            // this 指向Son 构造函数的对象实例
            Father.call(this, uname, age, score);
            this.score = score;
            // 使用call() 方法调用了Father() 方法,并且把其中的this 改变为Son() 方法的this 
        }
        // Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
        Son.prototype = new Father();
        // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
        Son.prototype.constructor = Son;
        // 这个是子构造函数专门的方法
        Son.prototype.exam = function () {
            console.log('考试');
        }
        var ty = new Son('唐嫣', 18, 61);
        console.log(ty);
        console.log(Father.prototype);
        console.log(Son.prototype.constructor); // 这个指向的是 Son 构造函数

6. 类的本质

ES6 之前通过 构造函数+原型实现面向对象编程

​ (1) 构造函数有原型对象prototype

​ (2) 构造函数原型对象prototype 里面有constructor 指向构造函数本身

​ (3) 构造函数可以通过原型对象添加方法

​ (4) 构造函数创建的实例对象有__proto__原型指向构造函数的原型对象

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

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

​ (1) 类有原型对象prototype.

​ (2) 类原型对象prototype 里面有constructor 指向类本身

​ (3) 类可以通过原型对象添加方法

7. ES6 中的新增方法

7.1 ES6 中的新增方法概述

竹摇是以下三个

  • 数组方法
  • 字符串方法
  • 对象方法

7.2 数组方法

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

7.2.1 数组遍历方法forEach()

array.forEach(function(currentValue,index,arr))

  • currentValue:数组当前项的值
  • index:数组当前的索引
  • arr:数组对象本身
var arr = [1, 2, 3];
        var sum = 0;
        arr.forEach(function (value, index, array) {
            console.log('数组元素' + value);
            console.log('数组索引' + index);
            console.log('数组本身' + array);
            sum += value
        });
        console.log(sum);

7.2.2 筛选数组filter()

array.filter(function(currentValue,index,arr))

  • filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
  • 注意它直接返回一个数组
  • currentValue:数组当前项的值
  • index:数组当前的索引
  • arr:数组对象本身
var arr = [10, 50, 4, 0, 56, 89];
        var newArr = arr.filter(function (value, index) {
            return value > 20
        })
        console.log(newArr);

7.2.3 查找数组中是否有满足条件的元素some()

array.some(function(currentValue,index,arr))

  • some() 方法用于检测数组中的元素是否满足指定条件,通俗点查找数组中是否有满足条件的元素
  • 注意它返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false
  • 如果找到第一个满足条件的元素,则终止循环,不在继续查找
  • currentValue:数组当前项的值
  • index:数组当前的索引
  • arr:数组对象本身
var arr = [10, 42, 47, 16, 19];
        var flag = arr.some(function (value, index) {
            return value > 17;
        })
        console.log(flag);

7.2.4 forEach和some的区别

var arr = [1, 2, 4, 9];
        // arr.forEach(function (value) {
        //     if (value == 2) {
        //         console.log('找到了');
        //         return true; // forEach 里面的return true 不会跳出循环   不会终止遍历/迭代
        //     }
        //     console.log(1);
        // });

        // 如果查询数组中唯一的元素,用some 方法更合适

        arr.some(function (value) {
            if (value == 2) {
                console.log('找到了');
                return true; // some 里面的return true 会跳出循环   会终止遍历/迭代    效率更高
            }
            console.log(1);
        });

7.3 字符串方法

trim() 去除字符串两边的空格

var str = '   abc   ';
console.log(str);  //    abc   
var str1 = str.trim();
console.log(str1); // abc

不能去除字符串中间的空格

var str = '   a b c   '
console.log(str); //    a b c   
var str1 = str.trim();
console.log(str1); // a b c

7.4 对象方法

Object.keys(obj) 用于获取对象自身所有的属性

  • 效果类似 for...in
  • 返回一个由属性名组成的数组
				// 用于获取对象自身所有的属性
        var obj = {
            age: 18,
            name: '李晓慧',
            price: Infinity,
        }
        var arr = Object.keys(obj);
        console.log(arr); // 对象的属性名

        arr.forEach(function (value) {
            console.log(value);
        })

8. 函数进阶

8.1 函数的定义和调用

8.1.1 函数的定义方式

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

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

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

        // 函数的定义方式

        // 1. 自定义函数(命名函数)
        function fn() {};

        // 2. 函数表达式(匿名函数)

        var fun = function () {}

        // 3. 利用 new Function('参数1','参数2','函数体')

        var f = new Function('a', 'b', 'console.log(a+b)');
        f(1, 2);

        // 4. 所有函数都是 Function 的实例(对象)
        console.dir(f);
				// 5. 函数也属于对象
        console.log(f instanceof Object); // true 这个是判断前者是否属于后者,f 属于对象

8.1.2 函数的调用方式

 				// 1. 普通函数
        function fn() {
            console.log('普通函数调用方式:函数名.()');
        }
        fn();

        // 2. 对象的方法
        var obj = {
            transfer: function () {
                console.log('对象的方法调用方式:对象.函数名()');
            }
        }
        obj.transfer();

        // 3. 构造函数
        function Star() {
            console.log('构造函数的使用方法:new 构造函数名()');
        }
        var fun = new Star();

        // 4. 绑定事件函数
        var btn = document.querySelector('button')
        btn.onclick = function () {
            console.log('绑定事件函数使用方法:1.先获取元素;2.Element.事件 = 函数;3.点击之后即可调用函数');
        } // 点击了之后就可以调用

        // 5. 定时器函数
        setInterval(function () {}, 1000); // 这个函数是一秒钟调用一次

        // 6. 立即执行函数
        (function () {
            console.log('立即自行函数调用方法:在后面()就可以实现自执行');
        })();
        // 立即执行函数是直接调用

8.1.3 this 的指向

// 函数的不同调用方式决定了this 的指向不同
        // 1. 普通函数 this 指向window
        function fn() {
            console.log('普通函数的this' + this);
        }
        window.fn();
        // 2. 对象的方法 this指向的是对象 o
        var o = {
            sayHi: function() {
                console.log('对象方法的this:' + this);
            }
        }
        o.sayHi();
        // 3. 构造函数 this 指向 ldh 这个实例对象 原型对象里面的this 指向的也是 ldh这个实例对象
        function Star() {};
        Star.prototype.sing = function() {

        }
        var ldh = new Star();
        // 4. 绑定事件函数 this 指向的是函数的调用者 btn这个按钮对象
        var btn = document.querySelector('button');
        btn.onclick = function() {
            console.log('绑定时间函数的this:' + this);
        };
        // 5. 定时器函数 this 指向的也是window
        window.setTimeout(function() {
            console.log('定时器的this:' + this);

        }, 1000);
        // 6. 立即执行函数 this还是指向window
        (function() {
            console.log('立即执行函数的this' + this);
        })();

8.1.4 改变函数内this 指向

1. call 方法

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

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

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

        // 改变函数内this 指向  js 提供了三种方法 call() apply() bind()
        // 1. call()
        var o = {
            name: 'andy'
        }

        function fn(a, b) {
            console.log(this);
            console.log(a + b);
        }

        fn.call(o, 1, 2)
        // call() 第一个可以调用函数 第二个可以改变函数内的this 指向
        // call() 的主要作用可以实现继承

        function Father(uname, age, sex) {
            this.uname = uname;
            this.age = age;
            this.sex = sex;
        }

        function Son( uname, age, sex) {
            Father.call(this, uname, age, sex)
        }
        var son = new Son('唐嫣', 20, '女');
        console.log(son);

2. apply 方法

fun.apply(thisArg,[argsArray])

// 改变函数内this 指向  js 提供了三种方法 call() apply() bind()
        // 2. apply() 方法    应用 运用的意思

        var obj = {
            name: 'marry'
        };

        function fn(arr) {
            console.log(this); // obj 
            console.log(arr); // ['john']
        }
        fn.apply(obj, ['john']);
        // 1. 也是调用函数 第二个可以改变函数内部的this 指向
        // 2. 但是他的参数必须是数组(伪数组)
        // 3. apply() 的主要应用 比如说我们可以利用apply 借助于数学内置对象求最大值
        // Math.max()
        var arr = [1, 2, 4, 22, 93, 56];
        // var max = Math.max.apply(null, arr); // null 不改变函数指向  但是null 可能有问题
        var max = Math.max.apply(Math, arr); // 可以指回Math  更加严格
        var min = Math.min.apply(Math, arr); // 可以指回Math  更加严格
        console.log(max, min);

3. bind 方法(用得较多)

fun.bind(thisArg,[argsArray])

				// 改变函数内this 指向  js 提供了三种方法 call() apply() bind()
        // 3. apply() 方法    捆绑 绑定的意思    不会去调用函数
        var obj = {
            name: 'andy'
        };

        function f(a, b) {
            console.log(this);
            console.log(a + b);
        };
        var fn = f.bind(obj, 5, 9)
        fn();
        // 1. 不会调用原来的函数,可以改变原来函数内部的this 指向
        // 2. 返回的是原函数改变this 之后的新函数
        // 3. 如果有的函数我们不需要立即调用,但是又想改变当前函数的指向此时用bind    
        // 4. 如果我们有一个按钮,点击之后禁用,三秒之后在开启
        // var btn = document.querySelector('button');
        // btn.onclick = function () {
        //     this.disabled = true; // 这个函数的this 指向的是当前函数的调用者 btn 按钮
        //     // var that = this;
        //     setTimeout(function () {
        //         // this.disabled = false; // 此时写this 是没有用的 定时器函数的this 是window 
        //         // that.disabled = false; // 常规做法  也可把that 手动换成btn
        //         this.disabled = false;
        //     }.bind(this), 3000)
        // }

        var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function () {
                this.disabled = true;
                setTimeout(function () {
                    this.disabled = false;
                }.bind(this), 2000)
            }
        }

4. 总结

9. 严格模式

9.1 什么是严格模式

9.2 开启严格模式

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

1. 为脚本开启严格模式

​ 为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句"use strict";(或'use strict')

    <script>
        "use strict" // 为整个脚本文件开启严格模式
    </script>
    <script>
        (function () {
            'use strict'// 为函数开启严格模式
        })()
    </script>

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

2. 为函数开启严格模式

​ 要给某个函数开启严格模式,需要把"use strict";声明放在函数体所有语句之前

    <script>
        function fn() {
            'use strict' // 下面的代码按照严格模式执行
        }

        function fun() {
            // 这里面的还是按照普通模式执行
        }
    </script>

10. 闭包

10.1 变量作用域

变量根据作用域的不同可以分成两种:全局变量和局部变量

  • 函数内部可以使用全局变量
  • 函数外部不可以使用局部变量
  • 当函数执行完毕,本作用域内的变量会销毁

10.2 什么是闭包

闭包是指有权访问另一个函数作用域中变量的函数。

简单理解就是,一个作用域可以访问另外一个函数内部的局部变量

        // 闭包:我们fun 这个函数作用域  访问了另外一个函数作用域内的局部变量 
        function fn() {
            var num = 10;

            function fun() {
                console.log(num);
            }
            fun();
        }
        fn();
        // 在一个函数里面嵌套一个函数,里面的函数需要用到外面函数定义的变量,将里面函数的 return 出来
        function inner() {
            var i = 10;

            function outer() {
                i++;
                console.log(i);
            }
            return outer;
        }
        var f = inner(); // 将函数的返回值 赋值给f变量 f变量的值是一个函数
        f(); // 11
        f(); // 12

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

11. 递归

11.1 什么是递归

如果一个函数可以在内部调用其自身,那么这个函数就是递归函数

函数内部自己调用自己,这个函数就是递归函数

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

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

        // 递归函数:函数内部自己调用自己
        var num = 1;

        function fn() {
            console.log('打印几句话');
            if (num == 6) {
                return; // 递归函数必须要加退出条件
            }
            num++;
            fn();
        }
        fn();

12. 浅拷贝和深拷贝

  1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝
  3. Object.assign(target,...sources)es6新增方法可以浅拷贝

12.1 浅拷贝

        // 1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
        // 2. 深拷贝拷贝多层,每一级别的数据都会拷贝
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            }
        };
        var o = {};
            // for循环赋值也算是一种浅拷贝
        for (var k in obj) {
            // k 是属性名    obj[k] 属性值  o.k
            o[k] = obj[k];
        }
        console.log(o);

对于更深层次的数据只是拷贝了地址,两个对象数据指向同一个地址,原来的对象修改了数据,拷贝过来的数据也会跟着改变。

        // 1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
        // 2. 深拷贝拷贝多层,每一级别的数据都会拷贝
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            }
        };
        var o = {};
        for (var k in obj) {
            // k 是属性名    obj[k] 属性值  o.k
            o[k] = obj[k];
        }
        console.log(o);
        o.msg.age = 20;
        console.log(obj);

        // 1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
        // 2. 深拷贝拷贝多层,每一级别的数据都会拷贝
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            }
        };
        var o = {};
        // for (var k in obj) {
        //     // k 是属性名    obj[k] 属性值  o.k
        //     o[k] = obj[k];
        // }
        Object.assign(o, obj)
        console.log(o);

12.2 深拷贝

深拷贝拷贝多层,每一级别的数据都会拷贝

        // 深拷贝拷贝多层,每一级别的数据都会拷贝
        // 可以用递归来做
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            },
            color: ['pink', 'red']
        };
        var o = {};
        // 封装函数
        function deepCopy(newObj, oldObj) {
            for (var k in oldObj) {
                // 判断我们的数据属于那种数据类型
                // 1. 获取属性值    oldObj[k]
                var item = oldObj[k];
                // 2. 判断这个值是否是数组
                // 需要把判断数组放在判断对象的前面,因为数组也属于对象
                if (item instanceof Array) {
                    newObj[k] = [];
                    deepCopy(newObj[k], item)
                } else if (item instanceof Object) {
                    // 3. 判断这个值是否是对象
                    newObj[k] = {};
                    deepCopy(newObj[k], item)
                } else {
                    // 4. 属于简单数据类型
                    newObj[k] = item;
                }
            }
        }
        deepCopy(o, obj);
        console.log(o);

        var arr = [];
        console.log(arr instanceof Object);
        o.msg.age = 20;
        console.log(obj);

深拷贝拷贝深层次数据时,会开辟一个新的内存空间来存放数据,修改原来数据对拷贝后的数据没有影响

13. 正则表达式

14. ES6 的新增语法

14.1 let 关键字

ES6 中新增的用于变量的关键字

  • let 声明的变量只在所处于的块级有效(块级作用域,内部有效)

注意:使用let 关键字声明的变量才具有块级作用域,使用var 声明的变量不具备块级作用域特性

if (true) {
  let a = 10;
  var b = 10;
}
console.log(b); // 10
console.log(a); // a is not defined
  • 防止循环变量变成全局变量(块级作用域)
// 防止循环变量变成全局变量
// for (var i = 0; i < 3; i++) {}
// console.log(i); // 3
for (let i = 0; i < 3; i++) {}
console.log(i); // i is not defined
  • 不存在变量提升
console.log(a); // a is not defined
let a = 10;
  • 暂时性死区

在块级作用域内使用let 声明的变量会被整体绑定在块级区,不再受外部代码影响

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

14.2 const 关键字

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

  • 具有块级作用域,同let 一样
if (true) {
    const a = 10;
    if (true) {
        const a = 20;
        console.log(a); // 20
        }
        console.log(a); // 10
}
console.log(a); // a is not defined

  • 声明常量时必须赋值

const PI; // Missing initializer in const declaration

  • 常量赋值后,值不能修改
// const PI = 3.14;
// PI = 10; // Assignment to constant variable.
const arr = [100, 200];
arr[0] = 10;
console.log(arr); // [10,200]
arr = [10, 20];
console.log(arr); // Assignment to constant variable.

// 对于复杂数据类型,可以修改内部值,但是不能重新赋值,重新赋值相当于修改了常量在内存中的地址,常量不可以更改地址

对于复杂数据类型,可以修改内部值,但是不能重新赋值,重新赋值相当于修改了常量在内存中的地址,常量不可以更改地址

14.3 let const var 的区别

14.4 解构赋值

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

1. 数组解构

数组解构允许我们按照一一对应的关系从数组中提取值然后将值赋值给变量

let arr = [1, 2, 3];

// 变量和值是一一对应的
// let [a, b, c] = arr;
// console.log(a); // 1
// console.log(b); // 2
// console.log(c); // 3

// 变量和值不是一一对应
let [a, b, c, d] = arr;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // undefined

2. 对象解构

对象解构允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量

let person = {
    name: 'lxh',
    age: 18,
}
let {
    name,
    age,
    sex
} = person;
console.log(name); // lxh
console.log(age); // 18
console.log(sex); // undefined

第二种写法

let person = {
    name: 'lxh',
    age: 18,
}
let {
    name: myName, // myName 相当于变量,接收person 里面和name 匹配的值
    age: myAge,
    sex: mySex
} = person;
console.log(myName); // lxh
console.log(myAge); // 18
console.log(mySex); // undefined

14.5 箭头函数

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

()=>{} // 定义箭头函数
// 箭头函数是用来简化函数定义语法
// 把箭头函数赋值给变量
const fn = () => {
  console.log(123);
}
fn(); // 123

函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

// 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
// function sum(num1, num2) {
//     return num1 + num2;
// }
// console.log(sum(1, 2)); // 3


// 箭头函数 省略大括号写法
const sum = (num1, num2) => num1 + num2;
console.log(sum(1, 2)); // 3

如果形参只有一个,可以省略小括号

// 如果形参只有一个,可以省略形参外面的小括号
// function sum(num1) {
//     return num1;
// }
// console.log(sum(1)); // 1

// 省略小括号写法
const sum = num1 => num1
console.log(sum(1)); // 1

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

function fn() {
    console.log(this); // {name: 'lxh'}
    return () => {
        console.log(this); // {name: 'lxh'}
    }
}
const obj = {
    name: 'lxh'
}
fn.call(obj)();

一个面试题

var obj = {
    age: 18,
    say: () => {
        console.log(this.age); // undefined 
        // 此时的this 指向的是window     obj 是一个对象,没有作用域,箭头函数的this 指向的是上下文中的this,指向的是window
    }
}

obj.say();
var name = 'lxh';
var obj1 = {
    name: 18,
    say: () => {
        console.log(this.name); // lxh 此时this 指向的是window,因为变量name 是全局变量,所以可以输出 
    }
}
obj1.say();

14.6 剩余参数

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

const sum = (...args) => { // ...args 接收所有的参数,箭头函数没有arguments 属性
    let total = 0;
    args.forEach(item => total += item);
    return total
}
console.log(sum(10, 20)); // 30
console.log(sum(10, 20, 30)); // 60

剩余参数和解构配合使用

// 剩余参数与解构一起使用
let arr = ['lxh', 'ljm', 'sxm'];
let [s1, ...s2] = arr;
console.log(s1); // lxh
console.log(s2); // ['ljm', 'sxm']

14.6 ES6 的内置对象扩展

1. Array 的扩展方法

1.1 扩展运算符...args

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

// 扩展运算符可以将数组拆分成以逗号分隔的参数序列
let arr = ['1', '2', '3'];
// ...arr // '1', '2', '3'
console.log(...arr); // 逗号在log 中被当作参数分隔符,不会显示在结果中
console.log('1', '2', '3');

扩展运算符可以应用于合并数组

// 扩展运算符可以用来合并数组
let arr1 = ['1', '2', '3'];
let arr2 = ['4', '5', '6'];
let arr3 = [...arr1, ...arr2]
console.log(...arr3); // 1 2 3 4 5 6
console.log(arr3); // ['1', '2', '3', '4', '5', '6']

// 合并数组方法2
arr1.push(...arr2)
console.log(arr1); // ['1', '2', '3', '4', '5', '6']

将类数组或可遍历对象转换为真正的数组

// 将类数组或可遍历对象转换为真正的数组
var div = document.querySelectorAll('div');
var arr01 = [...div];
console.log(arr01); // [div, div, div, div, div, div]

1.2 构造函数方法:Array.from()

构造函数方法:Array.from() 该方法的返回值是一个数组

var obj = {
    '0': 'lxh',
    '1': 'ljm',
    '2': 'hs',
    'length': 3
}
// 该方法的返回值是一个数组
var ary = Array.from(obj);
console.log(ary); // ['lxh', 'ljm', 'hs']

Array.from() 还可以传递第二个参数,第二个参数可以用函数对要被处理的对象的参数进行操作

// Array.from()    还可以传递第二个参数,第二个参数可以用函数对要被处理的对象的参数进行操作
var obj1 = {
    '0': '1',
    '1': '2',
    'length': 2
}
var arr = Array.from(obj1, item => item * 2)
console.log(arr);

1.3 find() 方法

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

// find() 方法  用于找出第一个符合条件的数组成员,如果没有找到返回undefined
var arr = [{
    id: 1,
    name: 'lxh'
}, {
    id: 2,
    name: 'ljm'
}];
var obj = arr.find(item => item.id == 2);
var obj1 = arr.find(item => item.id == 3);
console.log(obj); // {id: 2, name: 'ljm'}
console.log(obj1); // undefined

1.4 findIndex() 方法

findIndex() 方法 查找符合条件的第一个元素,返回当前元素的索引,如果没有就返回-1

// findIndex() 方法 查找符合条件的第一个元素,返回值为当前元素的索引,如果没有就返回-1
var arr = [0, 5, 10, 15, 20, 35];
var i = arr.findIndex(item => item > 10);
var j = arr.findIndex(item => item > 40);
console.log(i); // 3
console.log(j); // -1

1.5 includes() 方法

includes() 方法 表示某个数组是否包含给定的值,返回布尔值

// includes() 方法 表示某个数组是否包含给定的值,返回布尔值
var arr = [1, 2, 3];
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false

2. String 的扩展方法

1. 模板字符串

模板字符串中可以解析变量

// 模板字符串中可以解析变量
let name = `张三`;
let sayHello = `hello,我叫${name}`;
console.log(sayHello); // hello,我叫张三

模板字符串中可以换行

// 模板字符串中可以换行
let result = {
    name: 'lxh',
    age: 18
}
let html = `
<div>
    <span>${result.name}</span>
    <span>${result.age}</span>
</div>`;
console.log(html);

模板字符串可以调用函数

// 模板字符串可以调用函数
const Hello = function () {
    return 'hello';
};
let greet = `${Hello()},你好`;
console.log(greet);

2.实例方法

2.1 startsWith() 和endsWith()

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

endsWith():表示参数字符串是否在原字符串的尾部,放回布尔值

let str = 'Hello World!';
if (str.startsWith('Hello')) {
    console.log('以Hello开头');
    if (str.endsWith('!')) {
        console.log('以!结尾');
    }
}

2.2 repeat()

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

let str = 'Who are you';
let str01 = str.repeat(3);
console.log(str01);
console.log('abc'.repeat(3));

3. Set 数据结构

ES6 提供了新的数据结构Set ,它类似与数组,但是成员的值都是唯一的,没有重复的值

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

const s = new Set();

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

const set = new Set([1,2,3,4,5]);

const arr1 = new Set();
console.log(arr1.size); // 0     
const arr2 = new Set([1, 2, 3, 4, 5]);
console.log(arr2.size); // 5
const arr3 = new Set([1, 1, 3, 4, 4]);
console.log(arr3.size); // 3
// size 表示不同元素的个数,相同的元素只记作1,会把重复的元素 过滤掉

实例方法

  • add(value):添加某个值,返回Set 结构本身,一次只能添加一个参数,可以多次添加
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  • has(value):返回一个布尔值,表示该值是否为Set 的成员
  • clear():清除所有成员,没有返回值
const s1 = new Set([1]);
// 添加一个值
s1.add(2, ).add(3);
console.log(s1, s1.size);

// 删除一个没有的值 返回false
console.log(s1.delete(4)); // false
console.log(s1, s1.size);

// 删除存在的成员
console.log(s1.delete(3)); // true
console.log(s1, s1.size);

// 判断该值是否为Set 的成员
console.log(s1.has(1)); // true
console.log(s1.has(4)); // false

// 清除Set 所有成员
// s1.clear();

遍历

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

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

const s1 = new Set([1, 2]);
// 遍历Set 数据结构
s1.forEach(element => {
    console.log(element);
});

标签:function,console,函数,对象,js,var,高阶,log
来源: https://www.cnblogs.com/shzhuyao/p/15390240.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有