一、JS数据类型及区别?
基本数据类型:Number、String、Boolean、Undefined、Null
引用数据类型:Object、Array、Function
基本数据类型指的是简单的数据段,应用数据类型指的是有多个值构成的对象。
区别:
1、声明变量时的内存分配:
基本数据类型:存储在栈(stack)中的简单数据段,值直接存储在变量访问中的位置
引用数据类型:存储在堆(heap)中的对象,存储在变量处的是一个指针(point),指针指向对象在堆中的地址。
2、不同的内存分配规则带来的不同访问机制
基本数据类型:直接就能访问到
引用数据类型:通过指针访问,也就是按引用访问
3、复制变量时的不同
基本数据类型:复制值,
引用数据类型:复制指针
二、数据拷贝对两种数据类型怎么处理?
(对于基本数据类型,不存在深拷贝还是浅拷贝,赋值本身就是深拷贝;日常说的深浅拷贝是针对引用类型的)
浅拷贝
只拷贝对象的引用,而不深层次拷贝对象的值。多个对象指向堆内存中的同一个对象,任何一个对象的修改都会使得所有对象发生更改。
深拷贝
将引用类型的值全部拷贝一份,形成一个新的引用类型。
深拷贝的实现
1、JOSN.Stringify()、JOSN.parse()
支持多层
var obj1 = {
a: 1,
b: 2,
c: 3
}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 5;
console.log(obj1.a); // 1
console.log(obj2.a); // 5
2、展开运算符
不支持多层
let obj1 = {
a: 'a',
b: 'b'
};
// 第二种方式
let obj2 = {...obj1};
obj2.a='z'
console.log(obj2.a); // z
console.log(obj1.a); // a
3、Object.assign(target,source)
var obj1 = {
a: 1,
b: 2,
c: 3
}
var obj2 = Object.assign({}, obj1);
obj2.b = 5;
console.log(obj1.b); // 2
console.log(obj2.b); // 5
不支持多层
var obj1 = {
a: 1,
b: 2,
c: ['a','b','c']
}
var obj2 = Object.assign({}, obj1);
obj2.c[1] = 5;
console.log(obj1.c); // ["a", 5, "c"]
console.log(obj2.c); // ["a", 5, "c"]
三、数据类型的判断
1、typeOf
注意:在基本类型中,Null返回Object;在引用类型中,Function返回Function,其他都返回Object
console.log(typeof 7); // number
console.log(typeof '7'); // string
console.log(typeof false); // boolean
console.log(typeof [false]); // object
console.log(typeof function () {}); // function
console.log(typeof {}); // object
console.log(typeof undefined); // undefined
console.log(typeof null); // object
instanceOf
注意:其用来测试一个对象其原型链中是否存在某个构造函数的prototype属性。即判断对象是否是某一类型的实例。只有引用类型能精准判断。
console.log(7 instanceof Number); // false
console.log('7' instanceof String); // false
console.log(false instanceof Boolean); // false
console.log([] instanceof Array); //true
console.log(function () {} instanceof Function); // true
console.log({} instanceof Object); // true
// undefined is not a constructor
// null is not a constructor
四、关于typeOf返回值
typeOf( '123' ) // String
var a = '123'
typeOf( a ) // Object
五、对象和函数都有proto和prototype属性吗?
首先,我们需要牢记两点:
①__proto__和constructor属性是 对象 所独有的;
② prototype属性是 函数 所独有的。但是由于JS中函数也是一种对象,所以函数也拥有__proto__和constructor属性
__proto__属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象)
constructor属性是从一个对象指向一个函数,含义就是指向该对象的构造函数
prototype属性是从一个函数指向一个对象。它的含义是函数的原型对象,也就是用这个构造函数创建的实例的原型对象
“每个对象都有构造函数” 是指每个对象都可以找到其对应的constructor, 这个constructor可能是对象自己本身显式定义的或者通过__proto__在原型链中找到的 而单从constructor这个属性来讲,只有prototype对象才有。
函数创建的对象.__proto__ === 该函数.prototype
该函数.prototype.constructor === 该函数本身
__proto__属性的作用:
当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
prototype属性的作用:
就是让该函数所实例化的对象们都可以找到公用的属性和方法,即
f1.__proto__ === Foo.prototype。
constructor属性的含义:
就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。
六、介绍微任务和宏任务
微任务(microtask):
1、由JavaScript自身发起,先执行,不会触发新一轮的Tick
2、具体事件:
Promise
MutaionObserver
Object.observe(已废弃;Proxy 对象替代)
process.nextTick(Node.js)
宏任务(macrotask):
1、由宿主或浏览器发起,后执行,会触发新一轮的Tick
2、具体事件:
script (可以理解为外层同步代码)
setTimeout/setInterval
UI rendering/UI事件
postMessage,MessageChannel
setImmediate,I/O(Node.js)
案例练习
setTimeout(function(){
console.log('1');
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3');
}).then(function(){
console.log('4')
});
console.log('5');
// 2 5 3 4 1
案例分析
1.遇到setTimout,异步宏任务,放入宏任务队列中
2.遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
3.而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
4.遇到同步任务console.log(‘5’);输出5;主线程中同步任务执行完
5.从微任务队列中取出任务到主线程中,输出3、 4,微任务队列为空
6.从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空
七、CSS选择器权重
标签选择器、伪元素选择器:1
伪元素选择器为 :before :after
类选择器、属性选择器、伪类选择器:10
id选择器:100
行内样式
!important 级别最高
八、CSS定位及区别
relative相对定位
1、相对定位的偏移参考元素是元素本身,不会使元素脱离文档流。
2、元素的初始位置占据的空间会被保留。
3、相对定位元素常常作为绝对定位元素的父元素。
4、定位元素经常与z-index属性进行层次分级
absolute绝对定位
1、绝对定位元素以父辈元素中最近的定位元素为参考坐标,如果绝对定位元素的父辈元素中没有采用定位的,那么此绝对定位元素的参考对象是html,元素会脱离文档流,就好像文档流中被删除了一样。
2、定位元素经常与z-index属性进行层次分级。
fixed固定定位
1、位移的参考坐标是可视窗口,使用fixed的元素脱离文档流。
2、定位元素经常与z-index属性进行层次分级
注意:拖动滚动条就可以看到绝对定位和固定定位两个方式的区别,固定定位的元素如其名一样,能够固定在某个位置。而绝对定位就会随着滚动条滚动而移动位置。
九、什么是跨域?跨域怎么解决?
同源策略规定了三个东西必须一致:协议名、主机名、端口号。
解决跨域:
(1) cors:服务器返回时添加了部分响应请求头
(2) jsonp:但是只能解决get请求
(3) 配置代理服务器:nginx
十、什么是MVVM?
View是视图层,也就是用户界面。前端主要由HTML和CSS来构成,展现ViewModel或者Model层的数据。
Model是数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。
ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者从后端获取得到Model数据进行转换出来,做二次封装,以生成符合View层使用预期的视图数据模型。视图状态和行为都封装在ViewModel里。
十一、computed与watch的区别以及使用场景
computed:
1、computed内部的函数在调用时不加()。
2、computed是依赖vm中data的属性变化而变化的,也就是说,当data中的属性发生改变的时候,当前函数才会执行,data中的属性没有改变的时候,当前函数不会执行。
3、 computed中的函数必须用return返回。
4、在computed中不要对data中的属性进行赋值操作。如果对data中的属性进行赋值操作了,就是data中的属性发生改变,从而触发computed中的函数,形成死循环了。
5、当computed中的函数所依赖的属性没有发生改变,那么调用当前函数的时候会从缓存中读取。
使用场景:当一个值受多个属性影响的时候------------购物车商品结算
watch:
1、watch中的函数名称必须要和data中的属性名一致,因为watch是依赖data中的属性,当data中的属性发生改变的时候,watch中的函数就会执行。
2、 watch中的函数有两个参数,前者是newVal,后者是oldVal。
3、 watch中的函数是不需要调用的。
4、 watch只会监听数据的值是否发生改变,而不会去监听数据的地址是否发生改变。也就是说,watch想要监听引用类型数据的变化,需要进行深度监听。当采用深度监听时,就不能使用简便写法,要写成对象的形式。
5、特殊情况下,watch无法监听到数组的变化,特殊情况就是说更改数组中的数据时,数组已经更改,但是视图没有更新。更改数组必须要用splice()或者$set。
6、immediate:true // 实现页面首次加载的时候做一次监听。
使用场景:当一条数据的更改影响到多条数据的时候---------搜索框
十二、key的作用
1、key的作用主要是为了更高效的对比虚拟DOM中的某个节点是否是相同节点。
2、Vue在patch过程中判断两个节点是否是相同节点key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,Vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能。
3、实际使用中在渲染一组列表时key必须设置,而且必须是唯一标识,应该避免使用数组索引作为key,这可能导致一些隐蔽的bug;Vue中在使用相同标签元素过渡切换时,也会使用key属性,其目的也是为了让Vue可以区分它们,否则Vue只会替换其内部属性而不会触发过渡效果。
4、从源码中可以知道,Vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置,它的值就是是undefined,则可能永远认为这是两个相同节点,只能去做更新操作,这造成了大量的DOM更新操作,明显是不可取的。