如有错误,跪求指正!
JS
js数据类型有哪些?基本数据类型和引用数据类型的区别?
(1)基本数据类型:Number、String、Boolean、Null、 Undefined、Symbol(ES6)、BigInt(ES6),这些类型可以直接操作保存在变量中的实际值。
- 存放在栈中,储存值本省,复制值去赋值,存储值较小。
(2)引用数据类型:Object(在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象),存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。
- 存放在堆中,储存的是地址,复制地址去赋值,存储值较大
注意:BigInt和Number之间不能进行混合操作
深浅拷贝
- 浅拷贝
Object.assign():简单类型(如string, number)为深拷贝,当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝。- 扩展运算符(
...) Array.slice()Array.concat()
/**
* ES6实现只深拷贝第一级的方法
*/
//1: Object.assign()
var a = { name: "暖风" }
var b = Object.assign({}, a);
b.age = 18;
console.log(a.age);//undefined
//2: 数组
var a = [1, 2, 3];
var b = a.slice();
b.push(4);
console.log(a)//[ 1, 2, 3 ]
console.log(b)//[ 1, 2, 3, 4 ]
var a = [{ "k": [1, 2] }]
var b = a.slice();
b[0].k = [1, 2]
console.log(a)//[ { k: [ 1, 2 ] } ]
console.log(b)//[ { k: [ 1, 2 ] } ]
//3: concat
var a = [1, 2, 3];
var b = a.concat();
b.push(4);
console.log(a)//[ 1, 2, 3 ]
console.log(b)//[ 1, 2, 3, 4 ]
var a = [{ "k": [1, 2] }]
var b = a.slice();
b[0].k = [1, 2]
console.log(a)//[ { k: [ 1, 2 ] } ]
console.log(b)//[ { k: [ 1, 2 ] } ]
//4 :...
var a = [1, 2, 3];
var b = [...a];
b.push(4)
console.log(a)//[ 1, 2, 3 ]
console.log(b)//[ 1, 2, 3, 4 ]
var a = [{ "k": [1, 2] }];
var b = [...a];
console.log(a)//[ { k: [ 1, 2 ] } ]
console.log(b)//[ { k: [ 1, 2 ] } ]
- 深拷贝
1)JSON.parse(JSON.stringify(obj))
2)函数库 loadsh,cloneDeep
3)递归
// 递归
var a = { key1: "11111" }
function _copy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === "object") {
c[i] = (p[i].constructor === Array) ? [] : {}
_copy(p[i], c[i]);
} else {
c[i] = p[i]
}
}
return c;
}
a.key2 = ["小", "小"]
var b = {}
b = _copy(a, b);
b.key1 = "22"
b.key2.push("大");
console.log(a)
console.log(b)
js取整的方法,parselnt第二个参数是什么?
Math.trunc()去除数字的小数部分,保留整数部分;Math.round()返回一个数字四舍五入后的整数部分;Math.ceil()实现向上取整Math.floor()返回一个小于或等于数字的最小整数,即向下取整。parseInt("1010",2)参数:将字符串"1010"按二进制解析为十进制数Number.toFixed(0)
js作用域有哪些?
在 JavaScript 中,作用域是执行代码的上下文。
作用域有四种类型:全局作用域、函数作用域(也称“局部作用域”)、块作用域(block)和 eval 作用域
块级作用域
在 ES6 之前我们是没有块级作用域的,ES6 中的 let关键字 , const关键字 能形成块作用域
函数内部this指向有哪几种
理解不同形式的函数调用方式下的
this 指向,理解事件函数、定时函数中的 this 指向,函数的调用形式决定了 this 的指向。
总是指向函数的直接调用者。如果有 new关键字,this指向new出来的那个对象。
// 普通函数调用
function fn() {
console.log("普通函数调用", this); // 输出window
}
/*
* window.fn = function() {}
*/
fn(); // window
window.fn(); // window
// 构造函数调用
function Person() {
console.log("构造函数调用", this); // 输出Person对象,指向自己
}
var p1 = new Person();
// 对象方法调用
var obj = {
sayHi:function () {
console.log("对象方法调用", this); // 输出obj对象
}
};
obj.sayHi();
// 事件绑定调用
document.onclick = function () {
console.log("事件绑定调用方法" , this); // #document
}
// 定时器函数调用 window.setInterval
setInterval(function () {
console.log("定时器函数调用", this); // window
},1000)
如何改变this指向
1、利用call,apply,bind方法
2、通过变量将需要的 this 存储下来: let _this = this
3、借用其他对象的方法:借用构造函数、借用arguments
const obj = {
a: 100,
};
function sum(x, y) {
console.log(this.a + x + y);
}
sum(3,7);//underfined + 3 + 7 = NaN
//解析:直接调用,this指向window,window下面没有a属性,所以window.a是undefined
// 1、call
// 第一个参数是新的this指向, 从第二个参数开始表示函数自身的参数。
sum.call(obj, 3, 7); //100 + 3 + 7 = 110
// 2、apply
// 第一个参数是新的this指向, 第二个参数是一个数组或者类数组,里面的值依然是函数自身的参数。
sum.apply(obj, [3, 7]); //100 + 3 + 7 = 110
// 3、bind
// 第一个参数是新的this指向, 从第二个参数开始表示函数自身的参数,
// 但bind 是返回对应函数体,便于稍后调用,apply、call则是立即调用
sum.bind(obj, 3, 7)(); //100 + 3 + 7 = 110 注意这里需要再次调用
改变this指向的方法有哪些不同:
- call和apply的区别: 都是为了用一个本不属于一个对象的方法,让这个对象去执行。接受参数的方式不同;
- bind与call和apply的区别:
- 1.返回值的区别: bind的返回值是一个函数,而call和apply是立即调用。
- 2.参数使用的区别: bind与call一样是从第二个参数开始将想要传递的参数一一写入。但call是把第二个及以后的参数作为fn方法的实参传进去,而fn1方法的实参实则是在bind中参数的基础上再往后排。
什么是闭包,有什么应用场景
函数嵌套,内部函数使用了外部函数的变量(还包括外部函数的参数),外部函数返回了内部函数。
创建闭包最常见方式,就是在一个函数内部创建另一个函数。
应用场景:
- 防抖
- 节流
- 函数柯里化
- 公共方法或者包的封装,比如数据响应式原理,axios二次封装等
- 将变量和函数封装在一个作用域内,避免全局变量的污染,同时也可以隐藏一些细节
- 定时间setTimeout 传递的第一个函数不能带参数,通过闭包可以实现传参效果
function f1(a) {
function f2() {
console. log (a);
return f2;
}
var fun = f1(1);
setTimeout ( fun, 1000);//一秒之后打印出1
函数柯里化
闭包使得函数可以返回另一个函数作为结果,从而形成函数工厂的模式。 通过在内层函数中访问外层函数的参数或变量,可以创建具有不同参数或上下文的函数。这种技术称为柯里化,其目的在于避免频繁调用具有相同参数函数的同时,使代码更有维护性。
function createLogger(prefix) {
function logger(log) {
console.log(`${prefix}: ${log}`);
}
return logger;
}
const exportWarnning = createLogger('warnning');
exportWarnning('这是一个警告日志');
const exportError = createLogger('error');
exportError('这是一个错误日志');
内存泄漏
- 未及时释放闭包:及时解除引用
- 当闭包和其包含作用域之间存在循环引用时:尽量避免。
- 例如,如果闭包中引用了一个对象,而该对象又持有对闭包的引用,这将导致它们互相引用,无法被垃圾回收。
- 闭包中引用了全局变量,闭包将一直存在,即使在不再需要闭包时也无法释放:只在确实需要保留状态或隐藏数据时使用闭包,在不需要闭包的情况下,使用适当的作用域(例如局部变量或模块作用域)来防止不必要的内存占用。
Vue 中用到闭包的地方
- data 是一个函数,用data包裹属性
- 防止多组件实例对象之间公用一个data产生数据污染