JavaScript从入门到放弃 -(五)函数进阶(高级用法)

目录

  • 1. 函数的定义和调用
    • 1.1 函数定义的3种方式
    • 1.2 函数调用方式(常见的6种)
      • 1.2.1 普通函数的调用
      • 1.2.2 对象中的函数调用
      • 1.2.3 构造函数的调用
      • 1.2.4 DOM元素绑定事件调用函数
      • 1.2.5 定时器中调用函数
      • 1.2.6 立即执行函数
  • 2. 函数内部的this指向
    • 2.1 this指向
      • 2.1.1 普通函数
      • 2.1.2 对象中的函数
      • 2.1.3 构造函数
      • 2.1.4 绑定事件函数
      • 2.1.5 定时器函数
      • 2.1.6 立即执行函数
    • 2.2 改变函数内部this指向
      • 2.2.1 call方法
      • 2.2.2 apply方法
      • 2.2.3 bind方法(用得最多的一个方法)
      • 2.2.4 call、apply、bind总结
  • 3. 严格模式
    • 3.1 什么是严格模式
    • 3.2 开启严格模式
      • 3.2.1 为脚本开启严格模式
      • 3.2.2 为函数开启严格模式
      • 3.2.3 严格模式中的变化
    • 3.3 函数变化
  • 4. 高阶函数
    • 4.1 接收函数作为参数
    • 4.2 将函数作为返回值输出
  • 5. 闭包
    • 5.1 变量作用域
    • 5.2 什么是闭包
    • 5.3 闭包的作用
    • 5.4 闭包案例
      • 5.4.1 循环注册点击事件
      • 5.4.2 循环中的setTimeout(定时器)
      • 5.4.3 打车价格
  • 6. 递归
    • 6.1 什么是递归
    • 6.2 利用递归求数学题
      • 6.2.1 求1 * 2 * 3...* n 阶乘
      • 6.2.2 求斐波那契数列
      • 6.2.3 利用递归遍历数据
    • 6.3 浅拷贝与深拷贝
      • 6.3.1 浅拷贝
      • 6.3.2 深拷贝

学习目标:

  • 能够说出函数的多种定义和调用方式;
  • 能够说出和改变函数内部this的指向;
  • 能够说出严格模式的特点;
  • 能够把函数作为参数和返回值传递;
  • 能够说出闭包的作用;
  • 能够说出递归的两个条件;
  • 能够说出深拷贝和浅拷贝的区别。

1. 函数的定义和调用

1.1 函数定义的3种方式

  1. 函数声明方式 function 关键字(命名函数);
   function fn () {}
  1. 函数表达式(因为没有名字所以叫匿名函数);
   var fun = function () {}
  1. 利用 new Function()构造函数
 new Function("参数1","参数2",...,"函数体")`

注意:

  • Function 首字母大写,并且里面的参数必须是字符串格式
  • 第3种方式执行效率较低,也不方便书写,因此较少使用,仅做了解即可;
  • 所有函数都是Function的实例(对象)(因为所有的函数都是通过new Function得出来的)。

验证

var f = new Function('a','b','console.log(a+b)');
f(1,2);
// 所有函数都是Function的实例(对象)
console.dir (f);
console.log (f instanceof Object); // 返回True

关键字instanceof 的作用:判断前者(f)属不属于后者(Object

JavaScript从入门到放弃 -(五)函数进阶(高级用法)_第1张图片
所以,这里得出一个重要的结论函数也属于对象
也再一次印证了 js 里面的万物皆对象

到此为止,就可以画出函数的原型三角关系:
JavaScript从入门到放弃 -(五)函数进阶(高级用法)_第2张图片

1.2 函数调用方式(常见的6种)

1.2.1 普通函数的调用

function fn() {
	console.log('普通函数')
}
//调用方法
fn(); // 或者 fn.call()

1.2.2 对象中的函数调用

var o = {
	sayHi:function() {
		console.log('对象方法中的函数')
	}
}
// 调用方法
o.sayHi()

1.2.3 构造函数的调用

function Star() {}
// 调用方法
new Star();

1.2.4 DOM元素绑定事件调用函数

// 点击了按钮即可调用此函数
btn.onclick = function(){};

1.2.5 定时器中调用函数

// 定时器自动调用
setInterval(function(){},1000);  // 自动每隔 1 秒调用一次

1.2.6 立即执行函数

// 写法:()();
(function(){
	console.log('立即执行函数');
})();
// 立即执行函数是自动调用

2. 函数内部的this指向

2.1 this指向

这些this的指向,是当我们调用函数的时候确定的,调用方式的不同决定了this的指向不同,一般指向我们的调用者。
this指向出现的多种情况:

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

2.1.1 普通函数

function fn() {
	console.log('普通函数的this:' + this);
}
fn(); // 完整的写法:window.fn(); 即,指向函数的调用者

结果输出
JavaScript从入门到放弃 -(五)函数进阶(高级用法)_第3张图片

2.1.2 对象中的函数

var o = {
	sayHi: function() {
		console.log('对象方法的this:' + this);
	}
}
o.sayHi();  // 指向的是对象o

结果输出
在这里插入图片描述

2.1.3 构造函数

// 构造函数:指向ldh这个实例对象,原型对象里的this 指向的也是ldh这个实例对象
function Star() {};
Star.prototype.sing=function(){
}
var ldh= new Star();

2.1.4 绑定事件函数

// 4. 绑定事件函数
var btn = document.querySelector('button');
btn.onclick = function() {
 	console.log('绑定时间函数的this:' + this)
};   // 点击了按钮就可以调用这个函数

绑定事件函数this指向的是函数的调用者btn这个按钮对象
结果输出
JavaScript从入门到放弃 -(五)函数进阶(高级用法)_第4张图片

2.1.5 定时器函数

// 定时器函数
setInterval(function() {  // 完整的写法是 window.setInterval
	console.log('定时器的this:' +this);
}, 1000);

定时器函数的this指向也是window
结果输出
在这里插入图片描述

2.1.6 立即执行函数

// 立即执行函数
(function() {
	console.log('立即执行函数的this:' + this);
})();

立即执行函数 this 还是指向的window
结果输出
在这里插入图片描述

2.2 改变函数内部this指向

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

2.2.1 call方法

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

应用场景: 一般应用于继承.。

语法规范

fun.call(thisArg,arg1,arg2,...)
  • thisArg:目标对象;
  • arg1,arg2:传递的参数

示例

// 改变this 指向
var o = {
	name:'andy'
}

function fn() {
	console.log(this);
};
fn.call(o);

call 的作用:1)可以调用函数;2)可以改变函数内的this指向;3)可以实现继承(主要作用)。

2.2.2 apply方法

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

apply 一般应用于操作数组数据。

应用场景::经常跟数组有关系
比如:求最大值、最小值。
由于数组没有求最大值的方法(只有数学对象里面才有Math.max()),我们可以利用apply,借助于数学内置对象求最大值。

语法规范

fun.apply(thisArg,[argsArray])
  1. thisArg:在fun函数运行时指定的this值;
  2. argsArray:传递的值,必须包含在数组里面;
  3. 返回值就是函数的返回值,因为它就是调用函数。
var o = {
	name: 'andy'
}
 function fn(arr) {
      console.log(this);
      console.log(arr) // 输出的是字符串 'blue'
};
fn.apply(o,['blue'])
  1. apply可以改变函数内部this指向,并立即调用函数;
  2. 它传递的参数必须是数组形式(伪数组—— 即要加 [])。
// 求最大值
var arr = [1,66,3,99,4]// var imax = Math.max.apply(null,arr); // apply调用数学中的求最大值的方法。null 表示不改变this指向
// 参数 arr 传递给max
var imax = Math.max.apply(Math,arr); // this 重新指向Math
console.log(imax)  // 输出 99,成功找到最大值

2.2.3 bind方法(用得最多的一个方法)

bind() 方法不能调用函数,但是能改变函数内部this 指向,返回的是原函数改变this指向之后产生的新函数;

应用场景
如果有的函数我们不需要立即调用,但又想改变这个函数内部的this指向时。

语法结构

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

bind 的基本使用

var o = {
	name:'andy'
}
function fn(a,b){
	console.log(a + b);
}
var f=fn.bind(o,1,2);
f();

bind应用
点击按钮,禁用3秒后开启


var btn= document.querySelector('button');
btn.onclick = function() {
	this.disabled = true;      // this指向的是btn按钮
	setTimeout(){
		this.disabled = false; // 定时器函数里的this指向的是window
	}.bind(this),3000)         // 这个this因为写在计时器外面,所以它指向的是btn这个对象
}

2.2.4 call、apply、bind总结

相同点
都可以改变函数内部的this指向。

不同点

  1. callapply可以调用函数,并且改变函数内部this指向;
  2. callapply传递的参数不一样,call传递的参数是aru1aru2形式,而 apply 必须是数组形式[arg]
  3. bind 不能调用函数,但可以改变函数内部this指向。

主要应用场景

  1. call :经常做继承;
  2. apply :经常跟数组有关系,比如借助于数学对象Math.实现查找数组最大值和最小值。
  3. bind :如果不想调用函数,但是又想改变this指向时,比如改变定时器内部的this指向。

在不同应用场景,合理选用这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 脚本文件。

为整个脚本文件开户严格模式,只需要在所有语句之前放一个特定语句"use strict"; (或 ‘use strict’;),单引号、双引号均可。

<script>
   "use strict"; //开启了严格模式
   ...
</script>

因为 " use strict "加了引号,所以老版本浏览器会把它当作一行普通字符串而忽略。

严格模式要求比较多,比较规范,如果担心自己驾驭不了,可以仅为某一个函数开启严格模式。

3.2.2 为函数开启严格模式

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

function fn(){
  "use strict";
  return "123";
} 
//当前fn函数开启了严格模式

3.2.3 严格模式中的变化

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

  • 变量规定

    • 在正常模式中,如果变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量必须先用var声明,然后再使用。
    • 严禁删除已经声明的变量。比如,delete X; 语法是错误的
  • this指向问题

    • 正常模式下,全局作用域函数中的this指向window对象;
    • 严格模式下,全局作用域函数中的this指向undefined
    • 正常模式下,构造函数不加new可以当普通函数调用,this指向全局对象;
    • 严格模式下,如果构造函数不加new调用,由于this指向的是undefined,如果给它赋值,则会报错;
    • new实例化的构造函数指向创建的对象实例;
    • 定时器this仍是指向window

3.3 函数变化

  • 不能有重名的参数
  • 函数必须声明在顶层,新版本的JavaScript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
for (var i=0; i<5;i++) {
	function f2() {} // !!! 语法错误
}
function fn () {
	function fun(){} // 合法
}

另外,严格模式也不允许有8进制

更多严格模式要求参阅:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode

4. 高阶函数

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

函数可以当做参数来进行传递,回调函数

4.1 接收函数作为参数

<script>
function fn(callback) {
	callback && callback();
}
fn(function(){alert('hi')});
</script>

4.2 将函数作为返回值输出

<script>
function fn() {
	return function(){};
}
fn();
</script>

此时 fn 就是一个高阶函数。

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

实例
回调函数是高阶函数的一个运用

// 高阶函数- 函数可以作为参数传递
        function fn(a, b, callback) {
            console.log(a + b);
            callback && callback(); // 有callback传入进来,才调用 callback()
        }
        fn(1, 2, function() {
            console.log('我是最后调用的');
        });

补充说明:
实例中的callback && callback()语句,表示如果callback不存在,就不执行函数调用。如果 运算符是“||”,则不管callback存不存在都调用, 不存在就报错。

运算符 && 的作用,保持符号两边都为真,一个不为真就停止后面的脚本。

5. 闭包

JavaScript 中的 闭包异步,并称两大难点。

5.1 变量作用域

变量根据作用域的不同分为两种: 全局变量局部变量

  1. 在函数内部可以使用全局变量;
  2. 在函数外部不可以使用局部变量;
  3. 当函数执行完毕,本作用域内的局部变量就会销毁。

5.2 什么是闭包

闭包:指有权访问另一个函数作用域中变量的函数
——《JavaScript高级程序设计》
或简单理解为,一个作用域可以访问另外一个函数内部的局部变量

例如:

// fun这个函数作用域,访问了另外一个函数fn里面的局部变量num
function fn(){
	var num = 10;
	function fun(){
		console.log(num);
	}
	fun();
}
fn();

以上代码,就产生了闭包,而变量所在的函数fn,就是闭包函数。
在断点调试Scope 里,可清晰看到产生的过程
JavaScript从入门到放弃 -(五)函数进阶(高级用法)_第5张图片

5.3 闭包的作用

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

代码验证

// fn外面的作用域也可以访问fn内部的局部变量(利用了return)
function fn(){
	var num = 10;
	return function(){
		console.log(num);
	}
}
var f = fn();
f();

5.4 闭包案例

5.4.1 循环注册点击事件

HTML结构

	<ul class="nav">
        <li>榴莲li>
        <li>臭豆腐li>
        <li>鲱鱼罐头li>
        <li>大猪蹄子li>
    ul>

JavaScript 代码

1)用以前的方法获取当前li的索引号

var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) { // for是同步任务
            lis[i].index = i;
            lis[i].onclick = function() { // function是异步任务
                console.log(this.index);
            }
		}

2)利用闭包的方式获得当前li 的索引号(本案例面试中常用

   for (var i = 0; i < lis.length; i++) {
   // 利用for循环创建了4个立即执行函数
       (function(i) { // ②然后在这里传入变量"i"
           lis[i].onclick = function() {
                console.log(i);
           }
        })(i);        // ①首先在这里传入变量 "i"
   }

方式2 )使用立即执行函数(function(){})()产生了闭包。
但这种方法在本案例中,反而不如方式1)高效,且容易内存泄漏(如果一直不点击,那么变量i就不会被销毁)。

立即执行函数也叫小闭包

5.4.2 循环中的setTimeout(定时器)

要求:3 秒种之后,打印所有li元素的内容。

HTML结构

	<ul class="nav">
        <li>榴莲li>
        <li>臭豆腐li>
        <li>鲱鱼罐头li>
        <li>大猪蹄子li>
    ul>

JavaScript 代码

var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) { // for是同步任务
    (function(i) { // 先创建一个立即执行函数
       setTimeout(function() { // 是异步任务
           console.log(lis[i].innerHTML);
       }, 3000);
    })(i);
}

异步任务,主要有3种情况:
1)回调函数;
2)定时器中的回调函数;
3)事件中的回调函数。

5.4.3 打车价格

要求

  • 打车起步价13(3公里内);
  • 之后每多一公里增加5块钱,用户输入公里数就可以计算打车价格;
  • 如果有拥堵情况,总价格多收取10块钱拥堵费。

JavaScript 代码

<script>
        var car = (function() {
            var start = 13; // 起步价(局部变量)
            var total = 0; // 总价   (局部变量)
            return {
                // 正常的费用
                price: function(n) { // n:公里数
                    if (n <= 3) {
                        total = start;  
                    } else {
                        total = start + (n - 3) * 5
                    }
                    return total;
                },
                 // 拥堵之后的费用
                yd: function(flag) {
                    return flag ? total + 10 : total;
                }
            }
        })();
        console.log(car.price(5));; // 假设是5公里(n)
        console.log(car.yd(true));  // true:有拥堵情况
    </script>

6. 递归

6.1 什么是递归

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

简单理解:函数内部自己调用自己,这个函数就是递归函数。

<script>
	// 递归函数:函数内部自己调用自己
	function fn(){
		fn();
	}
	fn();
</script>

递归函数的作用和循环效果一样;
由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return

<script>
	// 递归函数:函数内部自己调用自己
	var num =1;
	function fn(){
		console.log('递归');
		if(num==6){
			return;  // 递归里面必须加退出条件
		}
		num ++ ;
		fn();
	}
	fn();
</script>

6.2 利用递归求数学题

6.2.1 求1 * 2 * 3…* n 阶乘

<script>
        // 利用递归函数求1~n的阶乘
        function fn(n) { // 用户输入几,就求 1~n之间的阶乘
            if (n == 1) {
                return 1;
            }
            return n * fn(n - 1);
        }
        console.log(fn(3)); // 假设用户输入3
    </script>

代码解析
假如用户输入的是3,

1)由于 if (n==1)判断为false,因此不执行return 1。而去执行后面的n * fn(n-1),传入3后的返回值为 3 * fn(3 - 2)。即return 3 * fn(2)

2)计算fn(2)
fn(2)相当于再次调用了fn的函数,那么此时里面的fn,传递的就是2了。相当于

return 3 * (2 * fn(2 - 1));  // 即f(2) 等价于 3 * (2 * fn(2 - 1))

fn(2 - 1)fn(1),亦即n =1。此时,经过判断 if(n==1),就执行return 1;

 	return 3 * (2 * 1) //  <=> return n * fn(n - 1)
 	// 最后,返回6

fn(2)计算过程

//return  3 * fn(2)
//return  3 * (2 * fn(1))
//return  3 * (2 * 1)
//return  3 * (2)
//return  6

总结
我们利用递归函数,求了1~n之间的阶乘。

主要思路是,不管是求1 ~ n 之间的阶乘,还是求1~n之间的累加和,都是用我们的前一项(n)乘以后一项(n - 1)。只不过,后一项需要用函数来重新执行一次;
递归函数还必须要加一个判断条件,和一个退出条件。如果正好处于第1项,就直接把第1项返回即可,不需要再进行阶乘或累加和。

6.2.2 求斐波那契数列

  • 利用递归求斐波那契数列(免子序列)1、1、2、2、3、5、8、13、21…
  • 用户输入一个数字n,就可以求出这个数字对应的免子序列值。

分析:

  1. 兔子序列中有一个规律,前两项相加的和,正好等于第三项;
  2. 只需要知道用户输入的 n 的前面两项(n-1n-2),就可以计算出n对应的序列值。

JavaScript 代码

<script>
        // 利用递归求斐波那契数列(免子序列)1、1、2、3、5、8、13、21...
        // 用户输入一个数字n 就可以求出这个数字对应的免子序列值。
        function fb(n) {
            if (n === 1 || n === 2) { // 给递归函数加一个退出的判断条件
                return 1;
            }
            return fb(n - 1) + fb(n - 2);
        }
        console.log(fb(4));  // 输出 3
        console.log(fb(8));  // 输出 21
</script>

6.2.3 利用递归遍历数据

var data = [{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                gname: '冰箱'
            }, {
                id: 12,
                gname: '洗衣机'
            }]
     }, {
            id: 2,
            name: '服饰'
}];

案例要求:根据上面创建的对象,要求输入 id 后,返回对应的数据对象

思路:
利用 forEach 去遍历里面的每一个对象。

forEach:可以遍历数组并得到数据中的每个元素。

这里采用封装函数来实现:

function getID(json, id) {
	  // 1、利用forEach遍历里面的每个数组对象
      json.forEach(function(item) {
      	//console.log(item); // 返回2个数组元素
      	if (item.id == id) {
         	console.log(item);
         	// 2、获取最里层的数据对象11、12利用递归函数
         	
      	}
     });
}
console.log(getID(data, 1));

但是,上面这个forEach 只能得到最外层的两个id12的两个对象。而对于里层的两个id分别为1112的对象却得不到。
这时,要想获取里层的1112,就可以把forEach或这个函数再执行一次,那么就利用到了递归函数

function getID(json, id) {
     json.forEach(function(item) {
         //console.log(item); // 返回2个数组元素
         if (item.id == id) {
            console.log(item);
            // 利用递归得到里层的数据11、12
            } else if (item.goods && item.goods.length > 0) {
                getID(item.goods, id);    //递归函数
         }
    });
}
getID(data, 1);
getID(data, 11);  // 输出 11对应的数据对象

从表面看,这里没有专门设置递归的退出条件。但是,由于使用了if …else…做执行递归的条件,因此不再需要单独的退出设置。

至此,已能打印出筛选后的数据对象。但需要实现的是将筛选得到的值返回,而不是打印,以给我们在某些情况下使用。因此,需要先保数据保存出来。

需要声明一个空对象 o ,用于保存筛选完成后的数据,完整函数如下:

function getID(json, id) {
    var o = {};
    json.forEach(function(item) {
         //console.log(item); // 返回2个数组元素
         if (item.id == id) {
             // console.log(item);
             o = item;
             return item;
            // 利用递归得到里层的数据11、12
         } else if (item.goods && item.goods.length > 0) {
            o = getID(item.goods, id);
         }
    });
    return o;
}
console.log(getID(data, 1));;
console.log(getID(data, 11));;

6.3 浅拷贝与深拷贝

  1. 浅拷贝:只是拷贝一层,对于更深层次的对象,它只拷贝了引用(地址);
  2. 深拷贝:拷贝多层,每一级别的数据都会拷贝;
  3. 浅拷贝(ES6新增):Object.assign(target,…sourcer)

ES6浅拷贝语法:

Object.assign(*target,...sourcer*) 
  • target:目标对象(拷贝给谁);
  • sourcer:拷贝对象(拷贝谁)。

6.3.1 浅拷贝

  var obj= {
      id:1,
      name:'andy'
   };
   var o={};

1)、利用原生 JavaScript 实现把对象obj的每一项拷贝给对象o
思路:用for...in 来遍历 obj 这个对象,把每一项拿出来给o

for (var k in obj) {
     // k 是属性名;obj[k]是属性值
     o[k]=obj[k]   // 赋值操作
}
console.log(o);

2)、利用ES6新增的浅拷贝方法

 Object.assign(o,obj);
 console.log(o);

实际开发中更 推荐 assign 方法实现浅拷贝。

6.3.2 深拷贝

<script>
        var obj={
            id:1,
            name:'andy',
            msg:{
                age:18
            }
        }
        var o={};
</script>

由于有多层数据,所以用递归更合适:先遍历外层数据(idname),再遍历里层数据(msg

封装函数实现深拷贝

 function deepCopy(newObj, oldObj) {
            for (var k in oldObj) {
                // 判断属性值属于简单数据类型还是复杂数据类型
                // 1、先拿到值才能判断——获取属性值 oldObj[k]
                var item = oldObj[k];
                // 2、判断这个值是否是数组;
                if (item instanceof Array) {
                    newObj[k] = []; // 相当于 o.color=[]
                    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);

上一篇:JavaScript从入门到放弃 -(四)E5 新增方法

下一篇:JavaScript从入门到放弃 -(六)正则表达式

你可能感兴趣的:(前端)