js注意事项1(数据类型内存深浅拷贝闭包this指向callapplybind方法)
Posted 嘿起屁儿整
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js注意事项1(数据类型内存深浅拷贝闭包this指向callapplybind方法)相关的知识,希望对你有一定的参考价值。
基本类型和引用类型
6大基本类型:number、string、boolean、null、undefined、symbol------在内存中占据空间小、大小固定。存在栈中,按值访问。
引用类型:对象、数组、函数 等(除了基本类型都是引用类型)-------在内存中空间大、大小不固定,因为数据量可能很多。存在堆中,按引用访问。
堆 栈内存
js中,内存分为 堆 、栈(ps:所有编程语言内存分配都差不多)
堆:存放大小不固定的数据,存放引用类型的具体数据
栈:存放大小固定的数据,存放值。所以可以将引用类型的地址值存在这里,但是引用类型的具体数据必须存在堆中。
入栈出栈:先进后出,后进先出(因为先放进去的在下面)
深拷贝和浅拷贝
浅拷贝:引用类型数据在复制后,改了引用类型数据,被拷贝方也会改变(原理:只拷贝第一层的数据到一个新对象上)
浅拷贝方法:
{ …xxx }
Object.assign({}, xxx)
var obja = { name: "张三" };
var objb = obja;
objb.name="李四";
console.log(obja); //李四
console.log(objb); //李四
深拷贝:引用数据类型在复制后,二者独立,不会因为改一个另一个也改变(原理:拷贝所有层数据到一个新对象上,完全独立互不影响)
深拷贝方法:
JSON.parse(JSON.stringtify( xxx ))
var obja = { name: "张三" };
var objb = JSON.parse(JSON.stringify(obja));
objb.name="李四";
console.log(obja); //张三
console.log(objb); //李四
var obja = { name: "张三" };
var objb = { ...obja };
objb.name = "李四";
console.log(obja); //张三
console.log(objb); //李四
var obja = { name: "张三" };
var objb = Object.assign({}, obja);
objb.name = "李四";
console.log(obja); //张三
console.log(objb); //李四
对象的浅拷贝与深拷贝
var obj = {a: 1, b: 2, c: { a: 3 },d: [4, 5]}
var obj1 = obj
var obj2 = JSON.parse(JSON.stringify(obj))//深拷贝常用方法
var obj3 = {...obj}
var obj4 = Object.assign({},obj)
obj.a = 999
obj.c.a = -999
obj.d[0] = 123
console.log(obj1) //{a: 999, b: 2, c: { a: -999 },d: [123, 5]}
console.log(obj2) //{a: 1, b: 2, c: { a: 3 },d: [4, 5]}
console.log(obj3) //{a: 1, b: 2, c: { a: -999 },d: [123, 5]}
console.log(obj4) //{a: 1, b: 2, c: { a: -999 },d: [123, 5]}
数组的浅拷贝与深拷贝
var arr = [1, 2, 3, [4, 5], {a: 6, b: 7}]
var arr1 = JSON.parse(JSON.stringify(arr))//深拷贝常用方法
var arr2 = arr
var arr3 = [...arr]
var arr4 = Object.assign([],arr)
console.log(arr === arr1) //false
console.log(arr === arr2) //true
console.log(arr === arr3) //false
console.log(arr === arr4) //false
arr[0]= 999
arr[3][0]= -999
arr[4].a = 123
console.log(arr1) //[1, 2, 3, [4, 5], {a: 6, b: 7}]
console.log(arr2) //[999, 2, 3, [-999, 5], {a: 123, b: 7}]
console.log(arr3) //[1, 2, 3, [-999, 5], {a: 123, b: 7}]
console.log(arr4) //[1, 2, 3, [-999, 5], {a: 123, b: 7}]
闭包
- 将变量放在函数外,就是全局变量,都能访问,就不存在闭包一说。
- 闭包:
- 1、让外部访问函数内部的变量或者函数;
- 2、避免全局变量过多,污染环境;
- 3、局部变量常驻内存,会造成内存泄漏;
- 4、每个闭包环境都是独立的,互不干扰
例子一:
function fn1(){
var a = 10;
return function(){
console.log(a);
}
}
var b = fn1();
b();
//10 因为返回一个函数,对于返回函数,a是全局变量
例子二:
function outerFn(){
var i = 0;
function innerFn(){
i++;
console.log(i);
}
return innerFn;
}
var inner = outerFn();
inner();
inner();
inner();
var inner2 = outerFn();
inner2();
inner2();
inner2();
//1 2 3 1 2 3 因为返回一个函数,对于函数,i是全局变量。闭包环境独立,互不影响,每次会在栈中开辟一个地址
例子三:
var i = 0;
function outerFn(){
function innnerFn(){
i++;
console.log(i);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();
//1 2 3 4 因为i是全局变量
例子四:
function fn(){
var a = 0;
return function(){
console.log(++a);
}
}
fn()();
fn()();
var a = fn();
a();
a();
//1 1,1 2 因为先返回函数,函数执行再返回a,所以有两个()() 。函数自执行属于闭包,互不影响。
例子五:
function outerFn(){
var i = 0;
function innnerFn(){
i++;
console.log(i);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();
//1 1 2 2 因为闭包开辟的新地址,两个函数互不影响
例子六:
var fn=(function(){
var i=10;
function fn(){
console.log(++i);
}
return fn;
})()
fn();
fn();
//11 12 因为返回的是函数所以加了个()执行函数才返回值,然后调用两次此函数
例子七:
function fun(a, b) {
console.log(b);
return {
fun: function (c) {
return fun(c, a);
}
};
}
var a = fun(0); //undefined 返回对象{fun:function....},此时,a为0,打印b没有
a.fun(1); //0 以0为基准,不管传入多少,其中的a已经定下来了
a.fun(2); //0
a.fun(3); //0
var b = fun(0).fun(1).fun(2).fun(3); //undefined 因为是对象形式,只能调用,没有这么多层
var c = fun(0).fun(1);// 0 最开始b为undefined,然后调用方法传入1,0,此时a为1,b为0
c.fun(2);//1 此时以1为基准,不管传入多少,其中a已经定了
c.fun(3);//1
this指向
- this指向最终调用者,没有就是window
- 箭头函数 不会改变this指向,直接指向他的父级
- new 出来的函数对象,this指向函数
案例1:
var name = "外国人";
function fn() {
var name = "小小飞";
console.log(this.name);
}
fn();
//外国人,因为this指向最终调用者,window(同样window默认省略)
案例2
var name = "外国人";
var obj= {
name: "小小飞",
fn: function () {
console.log(this.name);
}
}
obj.fn();
//小小飞,因为this指向最终调用者,fn(同样window默认省略)
案例3
var name = "外国人";
var a = {
name: "小小飞",
fn: function () {
console.log(this.name);
}
}
var f2= a.fn;
f2();
//外国人,因为this指向最终调用者window,然后f2是个函数
案例4
var name = "外国人";
var a = {
name: "小小飞",
fn: ()=> {
console.log(this.name);
}
}
a.fn();
//外国人,因为箭头函数中没有this指向,再往上一层找。即a的上层window
案例5
var name = "外国人";
function fn() {
var name = "小小飞";
console.log(this.name,this);
}
new fn();
//undefined fn(){} ,因为new的构造函数,this指向函数本身。然后函数没有name属性
call apply bind
- 这三个方法第一个参数都是绑定this指向,只是bind方法返回函数所以需要加上()执行一下
- 这三个方法其他参数都是传值,只是apply方法必须传数组,其他两个都是一一对应传就行
例子1:
var name = "小王", age = 18;
var obj = {
name: "小张",
objAge: console.log(this.age),
myfun: function () {
console.log(this.name + this.age)
}
}
obj.objAge; //18
obj.myfun();//小张undefined
因为objAge中的this指向window(上层), 函数中的this指向obj对象(上层)
例子2:
var food ="苹果"
function shows(){
console.log(this.food)
}
shows(); //苹果
因为函数中的this指向window(上层),food是全局变量所以随意访问
例子3:
var name = "小王", age = 18;
var obj = {
name: "小张",
objAge: console.log(this.age),
myfun: function () {
console.log(this.name + this.age)
}
}
var db = {
name: "鲁班",
age: "3"
}
obj.myfun.call(db) //鲁班3
obj.myfun.apply(db) //鲁班3
obj.myfun.bind(db)() //鲁班3
由此可见,call、applay、bind第一个参数都是绑定this指向,这里都指向db对象。只是bind返回函数,所以需要加个()执行函数
例子4:
var name = "小王", age = 18;
var obj = {
name: "小张",
objAge: console.log(this.age),
myfun: function (province, city) {
console.log(this.name + this.age, "来自" + province + city)
}
}
var db = {
name: "鲁班",
age: "3"
}
obj.myfun.call(db, "四川", "成都")
obj.myfun.apply(db, ["四川", "成都"])
obj.myfun.bind(db, "四川", "成都")()
由此可见,call、applay、bind第一个参数,都是绑定this指向,其他参数都是传值。
call和applay传值都是一一对应的,但是applay必须传数组格式
Finally , If there are some errors, please let me know
以上是关于js注意事项1(数据类型内存深浅拷贝闭包this指向callapplybind方法)的主要内容,如果未能解决你的问题,请参考以下文章