一、深拷贝与浅拷贝
深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。
(一)浅拷贝
只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
hasOwnProperty函数介绍
javaScript中hasOwnProperty函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
使用方法:
object.hasOwnProperty(proName)
其中参数object是必选项。一个对象的实例。
proName是必选项。一个属性名称的字符串值。如果 object 具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回 true,反之则返回 false。
var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
(二)深拷贝 (手撕代码)
深拷贝在计算机中开辟了一块内存地址用于存放复制的对象,
我们要求复制一个复杂的对象,那么我们就可以利用递归的思想来做
function copy(obj1,obj2){
//obj1是源内容,obj2放到要复制的对象中
var obj2=obj2||{}; //最初的时候给它一个初始值=它自己或者是一个json
for(var name in obj1){
if(typeof obj1[name] === "object"){ //先判断一下obj[name]是不是一个对象
obj2[name]= (obj1[name].constructor===Array)?[]:{}; //我们让要复制的对象的name项=数组或者是json
copy(obj1[name],obj2[name]); //然后来无限调用函数自己 递归思想
}else{
obj2[name]=obj1[name]; //如果不是对象,直接等于即可,不会发生引用。
}
}
return obj2; //然后在把复制好的对象给return出去
}
二、作用域
变量作用域:程序代码中定义这个变量的区域
函数作用域:属于这个函数的全部变量都可以在整个函数的范围内使用及复用
1.关于变量
javascript本身没有块级作用域的概念 ,使用var 声明的变量会自动被添加到最接近的环境中,如果初始化变量没有使用var,该变量会自动被添加到全局环境中。
任何声明在某个作用域内的变量,都将附属于这个作用域
function add(num1,num2){
var sum = num1+num2; //这里用var声明,sum是add函数的局部变量
return sum;
}
var result = add(10,20);//30
alert(sum); //由于sum不是有效的变量会导致错误
如果不用var声明呢
function add(num1,num2){
sum = num1+num2; //这里没有用var声明,sum是全局变量
return sum;
}
var result = add(10,20);//30
alert(sum); //30
2.函数作用域
首先了解一下函数声明和函数表达式的区别:比较直观的方法,看function关键字出现在生命中的位置,如果function是声明中的第一个词,那么就是一个函数声明,函数声明必须保证function关键字是第一个词,前面不能有任何符号
//函数声明
function functionName(){
}
//函数表达式
var functionName = function(){
}
包装函数
var a = 2;
(function foo(){ //函数表达式,foo只能在自己所在的函数内被访问,外部作用域无法访问,避免污染外部作用域
var a = 3;
console.log(a); //3
})();
console.log(a); //2
立即执行函数表达式(IIFE)
①( function foo(){ .. } ) ()
解释:第一个()将函数转化为表达式,第二个()执行了这个函数
②(function (){ . . }())
将调用括号放到包装括号内部
三、作用域链
全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找, 直至全局函数,这种组织形式就是作用域链。作用域链的顶端是当前执行代码所在环境的变量对象
(一)遍历作用域链的规则
规则:引擎从当前的执行作用域开始查找变量,如果找不到就向上一级继续寻找,当抵达最外层全局作用域时,无论找到还是没找到,查找过程都会停止
(二)欺骗(修改)词法作用域
eval
eval函数可以接收一个字符串参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。
function foo(str,a){
eval(str); //相当于var b = 3;
console.log(a,b);
}
var b = 2;
foo("var b = 3;",1); //1,3
实际上foo(..)在内部创建了一个b,修改了原来的词法作用域
with
width可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域
//重复调用obj
obj.a=2;
obj.b=3;
obj.c=4;
//用with实现
with(obj){
a=2;
b=4;
c=5;
}
四、变量提升
先看两段代码
a=2;
var a;
console.log(a); //输出2
//真实的执行过程
var a;
a=2;
console.log(a); //输出2
console.log(a);
var a=2; //输出undefined
//真实的执行过程
var a;
console.log(a);//输出undefined
a=2
这是为什么!!!!!
提升
变量和函数声明从他们在代码中出现的位置被“移动”到了最上面。这个过程叫做提升
现有声明后有赋值。
只有声明本身会被提升,而赋值或其他运行逻辑会留在原地,注意表达式和函数声明的区别
敲黑板!!!
当你看到var a=2;是,可能会认为这是一个声明,但javascript实际上会将其看成两个声明
- 第一个声明是在编译阶段进行的
var a;
- 第二个赋值声明会被留在原地等待执行阶段
a=2;
当出现多个“重复”声明的代码时,函数会被首先提升,然后才是变量。而且出现在后面的函数声明还是可以覆盖前面的
看一个题目
(1)
var a=[1,2,3];
function foo(){
a=[4,5,6];
}
alert(a);
//1,2,3
(2)
var a=[4,5,6];
(function(){
a.push(4,5,6);
})() ;
alert(a);
//4,5,6,4,5,6