面试题总结

239 阅读11分钟

1. js的数据类型有哪些,如何判断?

js的数据类型包括:

  • 1.null

  • 2.undefined

  • 3.boolean

  • 4.string

  • 5.number

  • 6.Symbol

判断js的数据类型方法:

1.typeof

typeof ''; // string 有效
typeof 1; // number 有效
typeof Symbol(); // symbol 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效

2.instanceof

instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。

instanceof (A,B) = {
    var L = A.__proto__;
    var R = B.prototype;
    if(L === R) {
        // A的内部属性 __proto__ 指向 B 的原型对象
        return true;
    }
    return false;
}

Tips:数组无法用instanceof判断,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型。 从原型链可以看出,[] 的 proto 直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,类似的 new Date()、new Person() 也会形成一条对应的原型链 。因此,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

3. constructor

当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。

可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。

Tips:

  1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。

  2. 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。

4. toString

toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。

对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用

2.Arry有哪些方法

  • concat()链接两个或多个数组;不改变原数组;返回被链接数组的一个副本

  • join()把数组元素放到一个字符串;不改变原数组;返回字符串

  • slice()从已有的数组中返回选定的元素;不改变原数组;返回一个新的数组

  • toString()把数组转为字符串;不改变原数组;返回一个新的数组

  • pop()删除数组最后一个元素,如果数组为空,则不改变数组,返回undefined;改变原有数组;返回被删除的元素

  • push()向数组末尾添加一个或多个元素;改变原数组;返回新数组的长度

  • reverse()颠倒数组中元素的顺序;改变原数组;返回该数组

  • shift()把数组的第一个元素删除,若为空数组,不进行操作,返回undefined;改变原数组;返回第一个元素的值

  • sort()对数组进行排序;改变数组;返回新数组

  • splice()从数组中添加/删除项目;改变数组;返回新数组

  • unshift()向数组的开头添加一个或多个个元素;改变原数组;返回新数组的长度。

3.简单实现字符串trim方法,去掉首位空格,有哪些方法?

<script type="text/javascript">
function trim(str){ //删除左右两端的空格
   return str.replace(/(^\s*)|(\s*$)/g, "");
}
function ltrim(str){ //删除左边的空格
    return str.replace(/(^\s*)/g,"");
}
function rtrim(str){ //删除右边的空格
    return str.replace(/(\s*$)/g,"");
}
</script>

4. Vue实现双向绑定的原理

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

5.为什么vue实例中的data属性要用函数返回

我们都知道基本数据类型存放在栈中,引用数据类型存放在堆中。

当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。

6.状态管理

vue中都包含了一个data()函数,用于要响应的组件。如果模板中使用的data()属性值发生改变,组件视图将会更新。

要想弄清楚状态管理,不得不说组件之间的传值。

1.组件通过props将数据传递给子组件:

props传值我们需要将一个值绑定在子组件的prop属性上。

通过上面代码可以看到ParentComponent组件把numbers数组作为同名的props传递给ChildComponent。然后ChildComponent组件进行渲染。

2.子组件通过自定义事件进行传值给父组件

通过上面代码可以看出来子组件通过自定义事件$emit('number-added', Number(number))"传值给父组件,父组件通过@number-added="numbers.push($event)"监听自定义事件接收子组件的传值。

上面说了如何父子之间的传值,但是兄弟组件的传值怎么办?

Vue大致有三种方式可以管理兄弟之间的通讯:

  • 使用全局的EventBus

EventBus是一个实例,用于支持独立组件之间发布和订阅自定义事件,换句话说EventBus是使用发布订阅模式进行数据的改变的。

  • 使用简单的全局储存

简单的全局存储很接近Flux的状态管理,在store中定义一个初始化对象state和一个addNumbers方法,

在组件中import { store } from '../store.js';引入store然后在data中直接获取数据,同时可以在methods中修改方法。

export default { 
    name: 'NumberDisplay', 
    data: () => ({ 
        storeState: store.state 
    }) ,
    methods: { 
        addNumber(numberInput) { 
            store.addNumber(Number(numberInput)) 
        } 
    }
}
  • 使用类似Flux库的Vuex

Vuex是一个Store库在这个库中可以实现各个组件数据的共享:

  • State方法:该方法中只是定义需要共享的数据;最简单的用法就是直接在组件的computed计算属性中调用this.$store.state.count;如果在组件中需要多次调用可以使用mapState来简化代码;
  • getter方法,在getter方法中可以过滤一下State里的数据从而获取到符合预期的数据。同时在组件中也可以使用mapGetters来批量获取计算属性。
  • mutation方法:在该方法中主要是修改State方法中的数据,默认第一个参数是State,在组件中使用commint方法调用this.$store.commit('hotel/setPickCity',data);(第一个参数为Store所在的位置,第二个为数据);如果同时要改变多个数据可以使用mapMutations辅助函数来操作;为了避免在mutation中异步同步同时使用,所以统一mutation方法中都是同步事件;
  • action方法:在该方法中只要是异步获取数据,然后使用commint调取mutation里的方法实现改变state里的方法。

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。在组建中我们使用this.$store.dispatch('xxx')来分发action,或者使用mapAction,进行多个方法的简化;

总的来说vuex中数据改变的方法大致就是:定义一个state函数后,getter函数可以过滤一些数据供其他组件使用,同时mutation来改变state里的数据,action通过调用mutation里的方法改变state数据。

7.computed、methods和watch的使用和区别

  • computed计算属性,简单的说computed就是可以处理任何复杂逻辑的方法类似于过滤器。在这个方法中可以按照我们的要求处理数据。computedgetset方法可以使用。

计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。

所以如果依赖不变,那么就不会调用computed相对来说性能比较好。

  • methods方法,这个属性简单的来说就是function,可以定义方法进行属性的修改或者返回。与computed相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
  • watch侦听属性,当有数据需要根据其他数据的变动而变动的时候就需要用watch进行侦听回调,使用的场景大多用以异步请求。

8.URL从渲染到页面展现发生了什么?

简单的说浏览器打开URL时发生了:

  • 第一步:DNS解析把域名解析成了IP地址,
  • 第二步:进行TCP协议传输,TCP三次握手
  • 第三步:发生HTTP请求,
  • 第四步:服务器除了请求并返回HTTP报文
  • 第五步:浏览器解析渲染页面
  • 第六步:断开连接:TCP四次回收 在这个过程中要明白URL是什么? URL:统一资源定位符,用于定位互联网上的资源,俗称网址; 一般遵循以下语法规则:
scheme://host.domain:port/path/filename
https://www.baidu.com/123
  • scheme:协议类型。常见的协议有httphttpsftpfilehttphttps的差别在于https是加密的,http1.0和1.1的差别是1.1支持多个请求对应一个TCP链接,http默认的端口是80https默认的是443

  • host:定义域主机。默认www还有其他二级域名;

  • domain:域名;

  • port:端口号;

  • path:路径;

  • filename:文件名称;

http请求分为三部分,TCP三次握手、http请求响应、关闭TCP连接。

  • TCP三次握手用于同步客户端和服务端序列号和确认号,并交换TCP窗口大小信息。为了避免失效的链接突然传入服务端而进行三次握手。

  • 三次握手后开始发送请求报文;请求报文包括:请求行、请求头、请求体。

    请求行包括:请求方法、URL、协议版本, 常用的http请求方法包括get、post、put、delet、head、options。 get是用于获取数据; post用于提交数据; put用于传输文件,报文主题包含文件内容,保存到对应的URL位置; head获取报文首部,与get类似,只不过返回报文主体,一般验证URL是否有效; delete用于删除文件,与put相反,删除对应的URL位置文件; option查询相应的URL支持的HTTP方法; getpost的差别: get是获取数据传输的数据比较小post是提交数据,传输的数据比较大; get把参数加到URL并且可以作为书签收藏,所以不安全,post相对来说比较安全;他们的最大的区别在于post不是幂等性而get是。 请求头:请求头包括请求的附加信息,由key/value组成,中间由冒号隔开,一般包含host,user-agent等; 请求体:name=tom&password=1234&realName=tomson包含多个请求参数,注意并不是所有请求都有请求数据。

  • 响应报文包括:响应行、响应头。响应体。

响应行包括:协议版本、状态、状态码描述 状态码描述规则:

  • 1xx:已接收,正在处理
  • 2xx:成功
  • 3xx:重定向,304成功 但是数据为改变
  • 4xx:客户端请求错误
  • 5xx:服务器端错误

响应头和请求头类似,由key/value组成,包含返回的数据格式等。

响应主体包含回车符、换行符和响应返回数据,并不是所有响应报文都有响应数据。

为了保证http请求的简单所以http请求是无状态的一种协议,这个无状态是指无登录状态,http不会记录是那个人发出的请求,这样的好处是使传输更加简单。

为了解决一些需要登录的请求就引入了cookiesession的概念;

cookie是一小段文本信息,http请求时会带上它然后服务器检查cookie传输不同的数据。

session是在服务器端记录用户状态的一个机制,Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

浏览器拿到响应的HTML文本后会根据浏览器的渲染机制进行渲染:

  • 根据HTML解析出DOM树
  • 根据css解析生成css规则树(css会阻塞dom的渲染和js的加载)
  • 结合DOM树和CSS规则树生成渲染树
  • 根据渲染树计算每一个节点信息
  • 根据计算好的信息绘制页面

断开连接发起TCP四次挥手。