面试题目

38 阅读9分钟

一、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.nextTickNode.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 Promisenew Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
3.Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
4.遇到同步任务console.log(‘5’);输出5;主线程中同步任务执行完
5.从微任务队列中取出任务到主线程中,输出34,微任务队列为空
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()或者$set6、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更新操作,明显是不可取的。