关于js中this指向问题和深拷贝,浅拷贝

关于js中this指向问题和深拷贝,浅拷贝

this的指向问题

在了解这个问题前,我们需要看下这两段代码:

var obj = {
  foo: function () { console.log(this.bar) },
  bar: 1
};

var foo = obj.foo;
var bar = 2;

obj.foo() // 1
foo() // 2

这种差异的原因,就在于函数体内部使用了this关键字。我们都知道,函数中有this的话,那就是谁调用函数,this就指向谁,按照阮一峰老师的说法就是this指向它的运行环境。对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。所以,两者的运行结果不一样。
为什么obj.foo()就是在obj环境执行,而一旦var foo = obj.foo,foo()就变成在全局环境执行?
JavaScript 语言之所以有this的设计,跟内存里面的数据结构有关系。

var obj = { foo:  5 };

上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。
也就是说,变量obj是一个地址(reference)。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

var f = function () {};
var obj = { f: f };

// 单独执行
f()

// obj 环境执行
obj.f()

JavaScript 允许在函数体内部,引用当前环境的其他变量。

var f = function () {
  console.log(this.x);
}

var x = 1;
var obj = {
  f: f,
  x: 2,
};

// 单独执行
f() // 1

// obj 环境执行
obj.f() // 2

上面代码中,函数f在全局环境执行,this.x指向全局环境的x。在obj环境执行,this.x指向obj.x。
回到本文开头提出的问题,obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

以上是通过阮一峰老师的博客介绍的!http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

深拷贝,浅拷贝

含义:假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

let a=[0,1,2,3,4],
    b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);

在这里插入图片描述
b复制了a,a的值变了,b的值也会跟着变化,这就是浅拷贝。
要弄清楚为什么,得了解基本数据类型与引用数据类型的概念了。

面试常问,基本数据类型有哪些,number,string,boolean,null,undefined,symbol以及未来ES10新增的BigInt(任意精度整数)七类。

引用数据类型(Object类)有常规名值对的无序对象{a:1},数组[1,2,3],以及函数等。

而这两类数据存储分别是这样的:

a.基本类型–名值存储在栈内存中,例如let a=1;
在这里插入图片描述
当你b=a复制时,栈内存会新开辟一个内存,例如这样:
在这里插入图片描述
所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了。当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。

b.引用数据类型–名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值,我们以上面浅拷贝的例子画个图:
在这里插入图片描述
当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。
在这里插入图片描述
而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了。

实现深拷贝方法
1.我们怎么去实现深拷贝呢,这里可以递归递归去复制所有层级属性。

function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);

在这里插入图片描述
2.除了递归,我们还可以借用JSON对象的parse和stringify

function deepClone(obj){
    let _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
    return objClone
}    
let a=[0,1,[2,3],4],
    b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

在这里插入图片描述
该文章引用了https://www.cnblogs.com/echolun/p/7889848.html

总结

this指向问题和深浅克隆都是和数据存储结构有关,this针对的是函数和对象函数,深浅拷贝针对的是对象属性,都是引用数据类型。
深拷贝是在堆里再开辟一个新的内存储存,再把新的地址给拷贝的新对象;
this是指向它的运行环境,但也有几种特殊函数不适用,比如定时器函数,this指向window,箭头函数指向上下文,而且他们的this指向不能变。
修改this指向的方法有三个:call(),apply(),bind()。(不适用与定时器和箭头函数)
相同点:都是改变this的指向,都能传参。
不同点:call()传递的参数用逗号隔开,立即执行;
apply传递参数为数组,立即执行;
bind()延迟执行,需要了再调用执行;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值