面试问题
字节提前两天约了面试,在晚上七点半,面试的岗位是前端高级开发工程师,面试官很和蔼首先上来做了下简单的自我介绍,然后看了简历的一段经历让我说了一下,问了我是倾向于前端还是后端,然后就进入了正式的面试环节。
typeof 和 instanceof 区别
instanceof 是否可以判断自定义的数据类型
1. typeof 用来判断数据的类型 typeof判断所有变量的类型 返回值有 string number boolean object function undefined
2. typeof 对于丰富的对象实例,只能返回"Object"字符串。
3. instanceof 用来判断对象,代码形式为obj1 instanceof obj2(obj1是否是obj2的实例),obj2必须为对象,否则会报错!其返回值为布尔值。
4. instanceof 可以对不同的`对象实例`进行判断,判断方法是根据对象的`原型链`依次向下查询,如果obj2的原型属性存在obj1的原型链上,(obj1 instanceof obj2)值为 true。
5. instanceof 是可以判断我们自定义的数据类型的
const Person = function(){}
var person = new Person()
person instanceof Person // true
person instanceof Object // true
vue
中数据绑定根据什么原理来改变 DOM
中的数据// vue 中实现的数据的双向绑定 其实使用了 订阅发布模式 使用 Object.defineProperty 来劫持监听各个属性的变化 ,
// 当属性发生变化后通知订阅者 触发更新回调函数 重新渲染
可以参考这个博客写的 vue 双向绑定原理
JavaScript中的每一个Function对象都有一个apply()方法和一个call()方法,它们的语法分别为:
定义:调用一个对象的一个方法,用另一个对象替换当前对象。
例如:B.apply(A, arguments)、B.call(A,args,args2);即使得 A 对象可以使用 B 对象的方法,改变了 B 对象 this 的指向,B 对象中原本 this 指向的是 B 对象,经过 call(thisObj,arg1,arg2...) 或者 apply(thisObj,arguments) 后 this 指向了 thisObj 。
且 如果 apply 和 call 中不传入任何参数 那么 this 指向了 Global(全局)
不同点
1. apply 最多只能传两个参数 第一个参数是新的 this 对象 第二个参数是一个参数数组,其他参数可以写进这个数组中。
2. call 它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。
3. 实际上,apply和call的功能是一样的,只是传入的参数列表形式不同。
4. 还有一个 bind() 方法 它的返回值是一个函数 需要调用执行
示例代码
// call()、apply()、bind() 重新指向对象的 this 第一个参数都是 this 的指向对象
var name="小王",age=17;// window 的属性
var obj={
name:"小张",
age:age,
Fun:function(fm,to){
console.log( this.name + "年龄:" + this.age + ";来自:" + fm +"; 去往:" + to);
},
child:{
name:"小张的儿子",
age:4,
say:function(fm,to){
console.log( this.name + "年龄:" + this.age + ";来自:" + fm +"; 去往:" + to);
}
}
}
var db={
name:"伽罗;",
age:23
}
// 输出
obj.Fun(); //小张年龄:17 this 指向了 定义它的 obj
obj.Fun.call();// 小王年龄:17 this 指向了 window
obj.Fun.call(db,"成都","上海");// 伽罗;年龄:23;来自:成都; 去往:上海 this 指向了 db
obj.Fun.apply(); // 小王年龄:17 this 指向了 window
obj.Fun.apply(db,["成都","上海"]);// 伽罗;年龄:23 this 指向了 db
obj.Fun.bind()();// 小王年龄:17 this 指向了 window
obj.Fun.bind(db,["成都","上海"],"aaa")();// 伽罗;年龄:23;来自:成都,上海; 去往:aaa this 指向了 db
5. 补充一个 arguments.callee
- argumnets 该 对象 代表正在 执行的函数和调用它的函数 的参数。是指函数调用传入的参数的数组 (类数组)
- arguments 有个属性 callee 是个指针 指向拥有这个 arguments 对象的函数。 应用:如 阶乘 setTimeout() 模拟 setInterval()
setTimeout(function(){
setTimeout(arguments.callee,2000)
},2000)
6. 容易混淆的 caller;
- caller 不是 arguments 的属性,它是 Function 的属性
funtion a(){
b();
}
funtion b(){
console.log(b.caller)
}
// 执行 a()
a() // 输出是 a 方法 顾明思议 b.caller 指 b 函数的调用者 即 谁调用了 b 在执行 a() 的时候 显然在 a 方法体中调用了 b 所以输出的是 a 方法
apply的一些其他巧妙用法
1.Math.max 可以实现得到数组中最大的一项:Math.max(1,2,3,4)
Math.max 不支持直接传入数组 比如 Math.max([1,2,3]) 这样是不对的 会输出 NaN
使用 apply 可以这样实现 Math.max.apply(null,[1,2,3,4]) // 输出 4 apply 会将数组转换成 一个接一个参数传递给方法。
同样 Math.min 是不是都可以这样呢? // 是的
2. Array.prototype.push 数组对象的原型实例方法push 提供了 push(item1,item2,...item) 同样我们也可以使用 apply 方法进行数组的合并 数组合并可以使用 contact
[].push.apply([1,2],[4,5]) // 返回的是新数组的长度 感觉实用性不强
// 使用es5 的语法 去实现 ES6 中的 const 的声明
// 首先我们要知道 const 有什么特性:
- 不能重新定义
- 不能重新赋值
- 语义化标识,表示声明后不可更改的不变量
- 声明时 完成 初始化
// 本质上:const 所保证的并不是变量的值不可改变 而是 变量所指向的内存地址不可改变 简单的数据类型保存在内存地址中 等同于常量
var _const=function(param,value){
// 使用一个对象 这里设置成 window
var _global = window;
// 判断要声明的 param 是否合法 不可以是关键字
var key_arr = ['class','var','let','const','return']// 还可以在增加限定关键字
if(!param || key_arr.indexOf(param)>-1){
throw new Error(`${param} 属于关键字 声明不合法`)
}
// 判断是否已经存在了
if(_global.hasOwnProperty(param)){
throw new Error(`${param} 已经被声明过了`)
}
// 给 _global 添加属性 param
_global[param] = value;
// 使用定义属性方法 defineProperty
Object.defineProperty(_global,param,{
get:function(){
return value;
},
set:function(){
// 检查赋值 已经存在了 不可再进行赋值
if(_global.hasOwnProperty(param)){
throw new Error(`${param} 不可再进行赋值`)
}
return value;
}
})
}
_const('TEST', "test_value");
console.log("TEST",TEST);
//_const('TEST', "test_value"); // Uncaught Error: TEST 已经被声明过了
TEST = "9999"//Uncaught Error: TEST 不可再进行赋值
console.log("TEST2",TEST);
()、[] 、{ }
的字符串 写代码做出检查 括号要成对// 检查合法性 对于一个字符检查括号的合法性 如 () [] {}
var checker = (str)=>{
var len = str.length;
if(len <= 1){
return false;
}
// 定义三个空数组 采用进栈出栈的方式去做判断
var arr_little = [] ,arr_middle = [], arr_big = [];
for(var i = 0 ; i < len ; i++){
if(str[i]==="("){
arr_little.push("(");
}else if(str[i]===")"){
if(arr_little.length==0){
return false;
}
arr_little.pop();
}else if(str[i]==="["){
arr_middle.push("[");
}else if(str[i]==="]"){
if(arr_middle.length==0){
return false;
}
arr_middle.pop();
}else if(str[i]==="{"){
arr_big.push("{");
}else if(str[i]==="}"){
if(arr_big.length==0){
return false;
}
arr_big.pop();
}else{
return false
}
}
// 判断 数组的个数为0
if(arr_little.length==0 && arr_middle.length==0 && arr_big.length == 0){
return true;
}
else
return false;
}
function randArr(arr){
let len=arr.length;
for(let i = 0;i < len;i++ ){
let index=parseInt(Math.random()*len-1);
let temp=arr[i];
arr[i]=arr[index];
arr[index]=temp;
}
return arr;
}
let arrNew=[1,2,3,4,5];
console.log(randArr(arrNew));
Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.
// Promise 是一种异步处理 有三种状态 pending (初始化状态) fulfilled (执行完成成功) rejected (执行失败)
// 当状态发生改变了就会去触发 then() 方法 无论是 pending-->fulfilled 还是 pending-->rejected 的状态都可以触发 then()
/* 优点
* 1 解决回调
* 2 解决嵌套
* 3 分离链式调用
*/
// 1. 实例化Promise
var myPromise = new Promise(function(resolve,reject) {
// body...
console.log("promise 的 创建完成 ");
setTimeout(function(){
resolve();
},3000)
});
myPromise.then(function(){
console.log("成功 回调")
})
// 浏览器的同源策略 即相同协议 端口 域名 下允许相互通信 三者有一个不同便会引起跨域问题
1.jsonp的方式,只针对于get请求
2. window.domain 在不同源中设置 相同的 window.domain 来实现跨域 但是局限于 基础域相同、协议端口都要相同
3. window.name+iframe 常用
在父页面上有个iframe嵌入了异源的html 异源的html中将数据赋值给window.name
父页面获取数据前,将iframe中的异源html替换为同源的html 此时里面的window.name 不会因为src改变而改变,所以就得到了来自异源的数据
4. CORS 服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
5. H5 中新引进的 window.postMessage()