前端学习第九天——深克隆
基本类型值和引用类型值
- 基本类型值:一个变量复制两一个变量的基本值,会在内存中进行一次克隆,会创建新的地址。
- 引用类型值:不会对堆内存进行克隆。
判断时的区别
- 基本类型进行相等判断时,会比较__值__是否相等。
- 引用类型值进行判断时,会比较__址__是否相等,也就是比较是否为内存中的同一个东西。
举例 | 当var a = b 变量传值时 | 当用==比较时 | |
---|---|---|---|
基本类型值 | 数字型、字符串型、 布尔型、 undefined型 | 内存中产生新的副本 | 比较值是否相等 |
引用类型值 | 对象、数组 | 内存中不产生新的副本,而 是让新变量指向同一个对象 | 比较内存地址是否相 同,即比较是否是同 一个对象 |
深克隆和浅克隆
使用arr1 = arr2 的语法不会克隆数组
- 浅克隆:只克隆数组的第一层,如果是多维数组,或者数组中的项是其他引用类型值,则不克隆其他层
- 深克隆:克隆数组所有层,要使用递归技术
函数
函数就是语句的封装,可以让封装的代码更方便点地复用,函数具有“以此定义,多次调用”的优点,使用函数,可简化代码,让代码更具可读性。
函数的定义
// function表示定义函数
// fun 是函数名
// ()形参列表
function fun(){
// 函数体语句
}
// 函数表达式写法(匿名函数)
var fun = function(){
// 函数体语句
}
函数声明的提升
fun();
// 预解析阶段会被提升
function fun(){
}
函数表达式写法的函数,没有提升特性
函数的优先提升
// 变量声明的提升,无法覆盖提升的函数
fun(); // 弹出B
var fun = function(){
alert('A');
}
funtion fun(){
alert("B");
}
// funtion fun()在预解析阶段执行过了,执行阶段不在执行,var fun 会在执行阶段赋值给fun()函数
fun(); // 弹出A
函数的参数
参数是函数内的一些特定值,在调用函数时,必须传入这些参数具体的值
arguments
函数内arguments
表示它接收到实参列表,它是一个类数组对象。
类数组对象:所有属性均从0开始的孜然数序列,并且有length属性,和数组类似可以用方括号书写下标访问对象的某个属性值,但不能调用函数的方法。
函数的返回值
return
关键字表示函数的返回值。预见return
语句则会立即退出函数
sort()
方法
数组排序可以使用sort()
方法,这个方法的参数是一个函数。
var arr = [33,22,11,55]
// a,b两个参数分别表示数组中靠前和靠后的项,如果需要将它们交换位置,则返回任意正数,否则就返回负数
arr.sort(function(a,b)){
return a - b;
});
递归
函数的内部语句可以调用函数自身,从而发起对函数逇一次迭代。在新的迭代中,又会执行调用函数自身的语句,从而又产生一次迭代。当执行到某一次时,不在进行迭代,函数呗一层一层返回,函数被递归。
- 递归的要素:
- 边界条件:确定递归到何时终止,也称为递归出口。
- 递归模式:大问题是如何分解为小问题的,也称递归体。
算法题
菲波那切数列:1、1、2、3、5、8、13、21。
function fib(n){
// 数列的下标为0的项和下标为1的项值为1
if(n==0 || n==1){
return 1;
}
// 每一项等于前面两项的和
return fib(n-1)+fib(n-2);
}
console.log(5)
实现深克隆
<script>
var arr1 = [33,11,22,55,[77,88]]
function deepClone(arr){
// 每一层都有一个结果数组
var result =[];
for(var i=0;i<arr.length;i++){
// 类型判断,如果是数组
if(Array.isArray(arr[i])){
result.push(deepClone(arr[i]))
}else{
// 遍历的项不是数组,递归的出口
result.push(arr[i])
}
}
// 返回结果数组
return result;
}
var arr2 = deepClone(arr1);
console.log(arr2);
// 验证深克隆
arr1[4].push(66);
console.log(arr1);
console.log(arr2);
</script>
全局变量和局部变量
JavaScript是函数级作用域编程语言:变量只在其定义时所在的function内部有意义
全局变量:如果不将变量定义在任何函数的内部,此时这个函数变量就是全局变量,它在任何函数内都可以被访问和更改
遮蔽效应: 函数中定义的变量和全局变量同名,则函数内部的变量会将全局的变量“遮蔽”
注意考虑变量声明提升的情况
var a = 10;
function fun(){
a++;
/*
变量声明提升,只是提升定义不提升值,相当于
var a;
a++;
var a = 5;
console.log(a);
局部变量a被自增1,a此时是undefined,自增1的结果是NaN。当程序执行到var a = 5;时,重新将a赋值为5
*/
var a = 5;
console.log(a); // 5
}
fun();
console.log(a); // 10
形参也是局部变量
var a = 10;
// 形参a也是函数内部的局部变量
function fun(a){
a++;
console.log(a); // 输出8
}
fun(7);
console.log(a); // 输出10
作用域链
一个函数内部也可以定义一个函数。和局部变量类似,定义在一个函数内部的函数是局部函数。
在函数嵌套中,变量会从内到外逐层寻找它的定义。
不加var将定义全局变量
初次给变量赋值时,如果不加var,则将定义全局变量
function fun(){
a = 3;
}
fun();
console.log(a); // 3
闭包
闭包(closure)是函数本身和改函书生命时所处的状态的组合。
函数能够记住其定义时所处的环境,即使函数不在其定义的环境被调用,也能访问定义时所处环境的变量。
- 在JavaScript中,每次创建函数都会创建闭包
- 闭包特性往往需要将函数换一个地方执行,才能被观查出来。
闭包允许我们将数据与操作该数据的函数关联起来。
function fun(){
var name = 'hello';
function innerfun(){
alert(name);
}
// 返回了内部函数
return innerfun;
}
// 内部函数被移动到了外部函数
var inn = fun();
inn();
- 闭包用途1——记忆性
当闭包产生时,函数所处环境的状态会始终保持在内存中,不会在外层函数调用后被自动清除。
<script>
function createCheckTemp(standradTemp){
function checkTemp(n){
if(n<=standradTemp){
alert('你的体温正常')
}
else{
alert('你的体温不正常')
}
}
return checkTemp;
}
// 创建一个checkTemp函数,它一37.1位标准线
var checkTemp_A = createCheckTemp(37.1);
checkTemp_A(37.2);
// 创建一个checkTemp函数,它一37.3位标准线
var checkTemp_B = createCheckTemp(37.3);
checkTemp_B(37.2);
</script>
- 闭包用途2——模拟私有变量
JavaScript中没有私有属性的概念,只能用闭包来模拟
<script>
// 封装一个函数,这个函数的功能就是私有化变量
function fun(){
// 定义一个局部变量a
var a = 0;
return function(){
alert(a);
}
}
var getA = fun();
getA();
</script>
不能滥用闭包,否则会造成网页的性能问题,严重时可能导致内存泄漏。所谓内存泄漏是指程序中已动态分配的内存由于某种原因未释放或无法释放。
闭包的一道面试题
function addCount(){
var count = 0;
return function(){
count = count + 1;
console.log(count);
};
}
var fun1 = addCount();
var fun2 = addCount();
fun1(); // 1
fun2(); // 1
fun2(); // 2
fun1(); // 2
IIFE
IIFE(Immediately Invoked Function Expression,立即调用函数表达式)是一种特殊的JavaScript函数写法,一旦被定义,就立即被调用。
// 第一个括号,将函数变为表达式
(function(){
}
// 最后的括号运行函数
)();
- 函数不能直接在后面加圆括号被调用。
- 函数必须转化为“函数表达式”才能被调用。可以用括号包裹,或者使用+、-、~。
IIFE的作用
- 为变量赋值
var age = 12;
var sex = '男';
var title = (function(){
if(age<18){
return '小朋友'
}else{
if(sex == '男'){
return '先生'
}else{
return '女士'
}
}
})();
- 将全局变量变为局部变量
var arr = [];
for(var i=0;i<5;i++){
arr.push(function(){
// i是一个全局变量,所有函数共享内存同一个变量i
alert(i);
});
}
arr[2](); // 弹出5
arr[3](); // 弹出5
IIFE可以在一些场合(如for循环中)将全局变量变为局部变量,语法显得紧凑。
var arr = [];
for(var i=0;i<5;i++){
(function(i){ // 形参i,闭包
arr.push(function(){
// 局部变量i
alert(i);
});
})(i);// 实参i
}
arr[2](); // 弹出2