js中有哪些数据类型,并解释清楚原始数据类型和引用数据类型
原始数据类型:null、undefined、string、number、boolean
引用数据类型:object
两者的区别:
1)值存储方式不同:
原始数据类型:将变量名和值都存储在栈内存中
引用数据类型:将变量名存储在栈内存中,将值存储在堆内存中,并在栈内存中存储值的地址,该地址指向堆内存中的值。
2)赋值方式不同:
当给b赋予另一个a的值若a值为原始数据类型,直接在栈内存中生成b值,两个变量以后进行值改变不会相互影响。若a值为引用数据类型,赋予b变量的是值地址,通过这个地址,两者指向的其实是堆内存中的同一个值,所以以后a,b任一变量对值进行改变,会直接影响另一个变量的值
如何复制一个对象的值?
function clone(obj) {
var oNew = new obj.constructor(obj.valueOf());
if (obj.constructor == Object) {
for (var i in obj) {
oNew[i] = obj[i];
if (typeof (oNew[i]) == 'object') {
clone(oNew[i]);
}
}
}
return oNew;
}
a.constructor可以判别标准数据类型(undefined和null除外)
bind,call,apply方法的使用,什么区别?什么时候用?
在调用bind方法改变this指向时,执行的方法不会执行,而调用call方法和apply方法时,在改变this指向的同时,执行的方法也会执行;
除了以上这一直观的区别外,在使用时传入入参格式也有区别,demo代码如下:
使用bind方法和call方法时入参格式为逗号分割的入参,使用apply方法时入参格式为一个数组,入参依次为数组元素,多个入参就在这一数组中依次添加;
依据个人开发习惯:
在绑定方法时多使用bind方法;
在调用其他实例方法时,多使用call方法或apply方法;
vue中v-model的实现原理
首先在页面初始化时候,vue的编译器会编译该html模板文件,将页面上的dom元素遍历生成一个虚拟的dom树。再递归遍历虚拟的dom的每一个节点。当匹配到其是一个元素而非纯文本,则继续遍历每一个属性。
如果遍历到v-model这个属性,则会为这个节点添加一个input事件,当监听从页面输入值(e.target.value)的时候,来更新vue实例中的data想对应的属性值。
同样初始化vue实例时候,会递归遍历data的每一个属性,并且通过defineProperty来监听每一个属性的get,set方法,从而一旦某个属性重新赋值,则能监听到变化从而操作相应的页面。
Vue中的data为什么是函数?
类比引用数据类型
Object是引用数据类型,如果不用function 返回,每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了;
javascipt只有函数构成作用域(注意理解作用域,只有函数的{}构成作用域,对象的{}以及 if(){}都不构成作用域),data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。
const MyComponent = function() {};
MyComponent.prototype.data = {
a: 1,
b: 2,
}
const component1 = new MyComponent();
const component2 = new MyComponent();
component1.data.a === component2.data.a; // true;
component1.data.b = 5;
component2.data.b // 5
如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改。
两个实例应该有自己各自的域才对,需要通过下面的方法来进行处理:
const MyComponent = function() {
this.data = this.data();
};
MyComponent.prototype.data = function() {
return {
a: 1,
b: 2,
}
};
这样一个实例的data属性都是独立的,不会相互影响了。
这都是因为js本身的特性带来的,跟vue本身设计无关。
说一说节流与防抖
主要是对于持续触发的事件,如:resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数,那就要用到节流与防抖。
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
function debounce(fn, times) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(context, args)
}, times);
}
}
let num = 0;
document.onmousemove = debounce(function() {
num++;
},1000);
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。
function throttle(fn, times) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > times) {
fn.apply(context, args);
previous = now;
}
}
}
let num = 0;
document.onmousemove = throttle(function() {
num++;
},1000);
给下面的这组数据按照name首字母进行分组
let arr = [{
name: 'aadgd',
age: 20
}, {
name: 'ryj',
age: 25
}, {
name: 'dsfg',
age: 27
}, {
name: 'dd',
age: 20
}, {
name: 'ddffe',
age: 20
}]
let que = [];
for (let i = 0; i < arr.length; i++) {
let brr = [];
brr.push(arr[i]);
for (let j = i + 1; j < arr.length; j++) {
if (arr[i].name.slice(0, 1) == arr[j].name.slice(0, 1)) {
brr.push(arr[j]);
arr.splice(j,1);
j--;
}
}
arr.splice(i,1);
i--;
que.push(brr)
}
console.log(que)
===========================================================
人生不过是一场修行,你我皆是过客。