关于前端:社招前端常见面试题附答案

27次阅读

共计 15756 个字符,预计需要花费 40 分钟才能阅读完成。

闭包

首先阐明什么是闭包,闭包简略来说就是函数嵌套函数,外部函数援用来内部函数的变量,从而导致垃圾回收
机制没有把以后变量回收掉,这样的操作带来了内存透露的影响,当内存透露到肯定水平会影响你的我的项目运行
变得卡顿等等问题。因而在我的项目中咱们要尽量避免内存透露。

AJAX

实现:利用 XMLHttpRequest

// get
const getJSON = (url) => {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();
        // open 办法用于指定 HTTP 申请的参数: method, url, async(是否异步,默认 true)xhr.open("GET", url, false);
        xhr.setRequestHeader('Content-Type', 'application/json');
        // onreadystatechange 属性指向一个监听函数。// readystatechange 事件产生时(实例的 readyState 属性变动),就会执行这个属性。xhr.onreadystatechange = function(){
            // 4 示意服务器返回的数据曾经齐全接管,或者本次接管曾经失败
            if(xhr.readyState !== 4) return;
            // 申请胜利,基本上只有 2xx 和 304 的状态码,示意服务器返回是失常状态
            if(xhr.status === 200 || xhr.status === 304) {
                // responseText 属性返回从服务器接管到的字符串
                resolve(xhr.responseText);
            }
            // 申请失败
            else {reject(new Error(xhr.responseText));
            }
        }
        xhr.send();});
}

// post
const postJSON = (url, data) => {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();
        xhr.open("POST", url);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function(){if(xhr.readyState !== 4) return;
            if(xhr.status === 200 || xhr.status === 304) {resolve(xhr.responseText);
            }
            else {reject(new Error(xhr.responseText));
            }
        }
        xhr.send(data);
    });
}

webpack 配置入口进口

module.exports={
    // 入口文件的配置项
    entry:{},
    // 进口文件的配置项
    output:{},
    // 模块:例如解读 CSS, 图片如何转换,压缩
    module:{},
    // 插件,用于生产模版和各项性能
    plugins:[],
    // 配置 webpack 开发服务性能
    devServer:{}}
简略形容了一下这几个属性是干什么的。形容一下 npm run dev / npm run build 执行的是哪些文件
通过配置 proxyTable 来达到开发环境跨域的问题,而后又能够扩大和他聊聊跨域的产生,如何跨域
最初能够在聊聊 webpack 的优化,例如 babel-loader 的优化,gzip 压缩等等

公布订阅模式(事件总线)

形容:实现一个公布订阅模式,领有 on, emit, once, off 办法

class EventEmitter {constructor() {
        // 蕴含所有监听器函数的容器对象
        // 内部结构: {msg1: [listener1, listener2], msg2: [listener3]}
        this.cache = {};}
    // 实现订阅
    on(name, callback) {if(this.cache[name]) {this.cache[name].push(callback);
        }
        else {this.cache[name] = [callback];
        }
    }
    // 删除订阅
    off(name, callback) {if(this.cache[name]) {this.cache[name] = this.cache[name].filter(item => item !== callback);
        }
        if(this.cache[name].length === 0) delete this.cache[name];
    }
    // 只执行一次订阅事件
    once(name, callback) {callback();
        this.off(name, callback);
    }
    // 触发事件
    emit(name, ...data) {if(this.cache[name]) {
            // 创立正本,如果回调函数内持续注册雷同事件,会造成死循环
            let tasks = this.cache[name].slice();
            for(let fn of tasks) {fn(...data);
            }
        }
    }
}

数组去重

应用 indexOf/includes 实现

function unique(arr) {var res = [];
    for(var i = 0; i < arr.length; i++) {if(res.indexOf(arr[i]) === -1) res.push(arr[i]);
        // if(!res.includes(arr[i])) res.push(arr[i]);
    }
    return res;
}

应用 filter(forEach) + indexOf/includes 实现

// filter
function unique(arr) {var res = arr.filter((value, index) => {
        // 只存第一个呈现的元素
        return arr.indexOf(value) === index;
    });
    return res;
}
// forEach
function unique(arr) {var res = [];
    arr.forEach((value) => {if(!res.includes(value)) res.push(value);
    });
    return res;
}

非 API 版本(原生)实现

function unique(arr) {var res = [];
    for(var i = 0; i < arr.length; i++) {
        var flag = false;
        for(var j = 0; j < res.length; j++) {if(arr[i] === res[j]) {
                flag = true;
                break;
            }
        }
        if(flag === false) res.push(arr[i]);
    }
    return res;
}

ES6 应用 Set + 扩大运算符(…)/Array.from() 实现

function unique(arr) {// return [...new Set(arr)];
    return Array.from(new Set(arr));
}

Number() 的存储空间是多大?如果后盾发送了一个超过最大本人的数字怎么办

Math.pow(2, 53),53 为有效数字,会产生截断,等于 JS 能反对的最大数字。

什么是尾调用,应用尾调用有什么益处?

尾调用指的是函数的最初一步调用另一个函数。代码执行是基于执行栈的,所以当在一个函数里调用另一个函数时,会保留以后的执行上下文,而后再新建另外一个执行上下文退出栈中。应用尾调用的话,因为曾经是函数的最初一步,所以这时能够不用再保留以后的执行上下文,从而节俭了内存,这就是尾调用优化。然而 ES6 的尾调用优化只在严格模式下开启,失常模式是有效的。

替换元素的概念及计算规定

通过批改某个属性值出现的内容就能够被替换的元素就称为“替换元素”。

替换元素除了内容可替换这一个性以外,还有以下个性:

  • 内容的外观不受页面上的 CSS 的影响:用业余的话讲就是在款式体现在 CSS 作用域之外。如何更改替换元素自身的外观须要相似 appearance 属性,或者浏览器本身裸露的一些款式接口。
  • 有本人的尺寸:在 Web 中,很多替换元素在没有明确尺寸设定的状况下,其默认的尺寸(不包含边框)是 300 像素×150 像素,如
  • 在很多 CSS 属性上有本人的一套体现规定:比拟具备代表性的就是 vertical-align 属性,对于替换元素和非替换元素,vertical-align 属性值的解释是不一样的。比方说 vertical-align 的默认值的 baseline,很简略的属性值,基线之意,被定义为字符 x 的下边缘,而替换元素的基线却被硬生生定义成了元素的下边缘。
  • 所有的替换元素都是内联程度元素:也就是替换元素和替换元素、替换元素和文字都是能够在一行显示的。然而,替换元素默认的 display 值却是不一样的,有的是 inline,有的是 inline-block。

替换元素的尺寸从内而外分为三类:

  • 固有尺寸: 指的是替换内容本来的尺寸。例如,图片、视频作为一个独立文件存在的时候,都是有着本人的宽度和高度的。
  • HTML 尺寸: 只能通过 HTML 原生属性扭转,这些 HTML 原生属性包含的 width 和 height 属性、的 size 属性。
  • CSS 尺寸: 特指能够通过 CSS 的 width 和 height 或者 max-width/min-width 和 max-height/min-height 设置的尺寸,对应盒尺寸中的 content box。

这三层构造的计算规定具体如下:
(1)如果没有 CSS 尺寸和 HTML 尺寸,则应用固有尺寸作为最终的宽高。
(2)如果没有 CSS 尺寸,则应用 HTML 尺寸作为最终的宽高。
(3)如果有 CSS 尺寸,则最终尺寸由 CSS 属性决定。
(4)如果“固有尺寸”含有固有的宽高比例,同时仅设置了宽度或仅设置了高度,则元素仍然依照固有的宽高比例显示。
(5)如果下面的条件都不合乎,则最终宽度体现为 300 像素,高度为 150 像素。
(6)内联替换元素和块级替换元素应用下面同一套尺寸计算规定。

HTTP 状态码 304 是多好还是少好

服务器为了进步网站访问速度,对之前拜访的局部页面指定缓存机制,当客户端在此对这些页面进行申请,服务器会依据缓存内容判断页面与之前是否雷同,若雷同便间接返回 304,此时客户端调用缓存内容,不用进行二次下载。

状态码 304 不应该认为是一种谬误,而是对客户端 有缓存状况下 服务端的一种响应。

搜索引擎蜘蛛会更加青眼内容源更新频繁的网站。通过特定工夫内对网站抓取返回的状态码来调节对该网站的抓取频次。若网站在肯定工夫内始终处于 304 的状态,那么蜘蛛可能会升高对网站的抓取次数。相同,若网站变动的频率十分之快,每次抓取都能获取新内容,那么与日俱增,的回访率也会进步。

产生较多 304 状态码的起因:

  • 页面更新周期长或不更新
  • 纯动态页面或强制生成动态 html

304 状态码呈现过多会造成以下问题:

  • 网站快照进行;
  • 收录缩小;
  • 权重降落。

如何进攻 XSS 攻打?

能够看到 XSS 危害如此之大,那么在开发网站时就要做好进攻措施,具体措施如下:

  • 能够从浏览器的执行来进行预防,一种是应用纯前端的形式,不必服务器端拼接后返回(不应用服务端渲染)。另一种是对须要插入到 HTML 中的代码做好充沛的本义。对于 DOM 型的攻打,次要是前端脚本的不牢靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能呈现的恶意代码状况进行判断。
  • 应用 CSP,CSP 的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行,从而避免恶意代码的注入攻打。
  1. CSP 指的是内容安全策略,它的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡由浏览器本人来实现。
  2. 通常有两种形式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的形式
  • 对一些敏感信息进行爱护,比方 cookie 应用 http-only,使得脚本无奈获取。也能够应用验证码,防止脚本伪装成用户执行一些操作。

对原型、原型链的了解

在 JavaScript 中是应用构造函数来新建一个对象的,每一个构造函数的外部都有一个 prototype 属性,它的属性值是一个对象,这个对象蕴含了能够由该构造函数的所有实例共享的属性和办法。当应用构造函数新建一个对象后,在这个对象的外部将蕴含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说不应该可能获取到这个值的,然而当初浏览器中都实现了 proto 属性来拜访这个属性,然而最好不要应用这个属性,因为它不是标准中规定的。ES5 中新增了一个 Object.getPrototypeOf() 办法,能够通过这个办法来获取对象的原型。

当拜访一个对象的属性时,如果这个对象外部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有本人的原型,于是就这样始终找上来,也就是原型链的概念。原型链的止境一般来说都是 Object.prototype 所以这就是新建的对象为什么可能应用 toString() 等办法的起因。

特点: JavaScript 对象是通过援用来传递的,创立的每个新对象实体中并没有一份属于本人的原型正本。当批改原型时,与之相干的对象也会继承这一扭转。

手写题:数组扁平化

function flatten(arr) {let result = [];

  for (let i = 0; i < arr.length; i++) {if (Array.isArray(arr[i])) {result = result.concat(flatten(arr[i]));
    } else {result = result.concat(arr[i]);
    }
  }

  return result;
}

const a = [1, [2, [3, 4]]];
console.log(flatten(a));

Promise 的根本用法

(1)创立 Promise 对象

Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已胜利)和 rejected(已失败)。

Promise 构造函数承受一个函数作为参数,该函数的两个参数别离是 resolvereject

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作胜利 */){resolve(value);
  } else {reject(error);
  }
});

个别状况下都会应用 new Promise() 来创立 promise 对象,然而也能够应用 promise.resolvepromise.reject这两个办法:

  • Promise.resolve

Promise.resolve(value)的返回值也是一个 promise 对象,能够对返回值进行.then 调用,代码如下:

Promise.resolve(11).then(function(value){console.log(value); // 打印出 11
});

resolve(11)代码中,会让 promise 对象进入确定 (resolve 状态),并将参数 11 传递给前面的 then 所指定的onFulfilled 函数;

创立 promise 对象能够应用 new Promise 的模式创建对象,也能够应用 Promise.resolve(value) 的模式创立 promise 对象;

  • Promise.reject

Promise.reject 也是 new Promise 的快捷模式,也创立一个 promise 对象。代码如下:

Promise.reject(new Error(“我错了,请原谅俺!!”));

就是上面的代码 new Promise 的简略模式:

new Promise(function(resolve,reject){reject(new Error("我错了!"));
});

上面是应用 resolve 办法和 reject 办法:

function testPromise(ready) {return new Promise(function(resolve,reject){if(ready) {resolve("hello world");
    }else {reject("No thanks");
    }
  });
};
// 办法调用
testPromise(true).then(function(msg){console.log(msg);
},function(error){console.log(error);
});

下面的代码的含意是给 testPromise 办法传递一个参数,返回一个 promise 对象,如果为 true 的话,那么调用 promise 对象中的 resolve() 办法,并且把其中的参数传递给前面的 then 第一个函数内,因而打印出“hello world”, 如果为 false 的话,会调用 promise 对象中的 reject() 办法,则会进入 then 的第二个函数内,会打印No thanks

(2)Promise 办法

Promise 有五个罕用的办法:then()、catch()、all()、race()、finally。上面就来看一下这些办法。

  1. then()

当 Promise 执行的内容合乎胜利条件时,调用 resolve 函数,失败就调用 reject 函数。Promise 创立完了,那该如何调用呢?

promise.then(function(value) {// success}, function(error) {// failure});

then办法能够承受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数能够省略。then办法返回的是一个新的 Promise 实例(不是原来那个 Promise 实例)。因而能够采纳链式写法,即 then 办法前面再调用另一个 then 办法。

当要写有程序的异步事件时,须要串行时,能够这样写:

let promise = new Promise((resolve,reject)=>{ajax('first').success(function(res){resolve(res);
    })
})
promise.then(res=>{return new Promise((resovle,reject)=>{ajax('second').success(function(res){resolve(res)
        })
    })
}).then(res=>{return new Promise((resovle,reject)=>{ajax('second').success(function(res){resolve(res)
        })
    })
}).then(res=>{})

那当要写的事件没有程序或者关系时,还如何写呢?能够应用all 办法来解决。

2. catch()

Promise 对象除了有 then 办法,还有一个 catch 办法,该办法相当于 then 办法的第二个参数,指向 reject 的回调函数。不过 catch 办法还有一个作用,就是在执行 resolve 回调函数时,如果呈现谬误,抛出异样,不会进行运行,而是进入 catch 办法中。

p.then((data) => {console.log('resolved',data);
},(err) => {console.log('rejected',err);
     }
); 
p.then((data) => {console.log('resolved',data);
}).catch((err) => {console.log('rejected',err);
});

3. all()

all办法能够实现并行任务,它接管一个数组,数组的每一项都是一个 promise 对象。当数组中所有的 promise 的状态都达到 resolved 的时候,all办法的状态就会变成 resolved,如果有一个状态变成了rejected,那么all 办法的状态就会变成rejected

javascript
let promise1 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(1);
    },2000)
});
let promise2 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2);
    },1000)
});
let promise3 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(3);
    },3000)
});
Promise.all([promise1,promise2,promise3]).then(res=>{console.log(res);
    // 后果为:[1,2,3] 
})

调用 all 办法时的后果胜利的时候是回调函数的参数也是一个数组,这个数组按程序保留着每一个 promise 对象 resolve 执行时的值。

(4)race()

race办法和 all 一样,承受的参数是一个每项都是 promise 的数组,然而与 all 不同的是,当最先执行完的事件执行完之后,就间接返回该 promise 对象的值。如果第一个 promise 对象状态变成 resolved,那本身的状态变成了resolved;反之第一个promise 变成rejected,那本身状态就会变成rejected

let promise1 = new Promise((resolve,reject)=>{setTimeout(()=>{reject(1);
    },2000)
});
let promise2 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2);
    },1000)
});
let promise3 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(3);
    },3000)
});
Promise.race([promise1,promise2,promise3]).then(res=>{console.log(res);
    // 后果:2
},rej=>{console.log(rej)};
)

那么 race 办法有什么理论作用呢?当要做一件事,超过多长时间就不做了,能够用这个办法来解决:

Promise.race([promise1,timeOutPromise(5000)]).then(res=>{})

5. finally()

finally办法用于指定不论 Promise 对象最初状态如何,都会执行的操作。该办法是 ES2018 引入规范的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

下面代码中,不论 promise 最初的状态,在执行完 thencatch指定的回调函数当前,都会执行 finally 办法指定的回调函数。

上面是一个例子,服务器应用 Promise 解决申请,而后应用 finally 办法关掉服务器。

server.listen(port)
  .then(function () {// ...})
  .finally(server.stop);

finally办法的回调函数不承受任何参数,这意味着没有方法晓得,后面的 Promise 状态到底是 fulfilled 还是 rejected。这表明,finally 办法外面的操作,应该是与状态无关的,不依赖于 Promise 的执行后果。finally实质上是 then 办法的特例:

promise
.finally(() => {// 语句});
// 等同于
promise
.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);

下面代码中,如果不应用 finally 办法,同样的语句须要为胜利和失败两种状况各写一次。有了 finally 办法,则只须要写一次。

CSS 优化和进步性能的办法有哪些?

加载性能:

(1)css 压缩:将写好的 css 进行打包压缩,能够减小文件体积。

(2)css 繁多款式:当须要下边距和右边距的时候,很多时候会抉择应用 margin:top 0 bottom 0;但 margin-bottom:bottom;margin-left:left; 执行效率会更高。

(3)缩小应用 @import,倡议应用 link,因为后者在页面加载时一起加载,前者是期待页面加载实现之后再进行加载。

选择器性能:

(1)要害选择器(key selector)。选择器的最初面的局部为要害选择器(即用来匹配指标元素的局部)。CSS 选择符是从右到左进行匹配的。当应用后辈选择器的时候,浏览器会遍历所有子元素来确定是否是指定的元素等等;

(2)如果规定领有 ID 选择器作为其要害选择器,则不要为规定减少标签。过滤掉无关的规定(这样款式零碎就不会浪费时间去匹配它们了)。

(3)防止应用通配规定,如 *{}计算次数惊人,只对须要用到的元素进行抉择。

(4)尽量少的去对标签进行抉择,而是用 class。

(5)尽量少的去应用后辈选择器,升高选择器的权重值。后辈选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的应用类来关联每一个标签元素。

(6)理解哪些属性是能够通过继承而来的,而后防止对这些属性反复指定规定。

渲染性能:

(1)谨慎应用高性能属性:浮动、定位。

(2)尽量减少页面重排、重绘。

(3)去除空规定:{}。空规定的产生起因一般来说是为了预留款式。去除这些空规定无疑能缩小 css 文档体积。

(4)属性值为 0 时,不加单位。

(5)属性值为浮动小数 0.**,能够省略小数点之前的 0。

(6)标准化各种浏览器前缀:带浏览器前缀的在前。规范属性在后。

(7)不应用 @import 前缀,它会影响 css 的加载速度。

(8)选择器优化嵌套,尽量避免层级过深。

(9)css 雪碧图,同一页面相近局部的小图标,方便使用,缩小页面的申请次数,然而同时图片自身会变大,应用时,优劣思考分明,再应用。

(10)正确应用 display 的属性,因为 display 的作用,某些款式组合会有效,徒增款式体积的同时也影响解析性能。

(11)不滥用 web 字体。对于中文网站来说 WebFonts 可能很生疏,国外却很风行。web fonts 通常体积宏大,而且一些浏览器在下载 web fonts 时会阻塞页面渲染伤害性能。

可维护性、健壮性:

(1)将具备雷同属性的款式抽离进去,整合并通过 class 在页面中进行应用,进步 css 的可维护性。

(2)款式与内容拆散:将 css 代码定义到内部 css 中。

代码输入问题

function Parent() {
    this.a = 1;
    this.b = [1, 2, this.a];
    this.c = {demo: 5};
    this.show = function () {console.log(this.a , this.b , this.c.demo);
    }
}

function Child() {
    this.a = 2;
    this.change = function () {this.b.push(this.a);
        this.a = this.b.length;
        this.c.demo = this.a++;
    }
}

Child.prototype = new Parent();
var parent = new Parent();
var child1 = new Child();
var child2 = new Child();
child1.a = 11;
child2.a = 12;
parent.show();
child1.show();
child2.show();
child1.change();
child2.change();
parent.show();
child1.show();
child2.show();

输入后果:

parent.show(); // 1  [1,2,1] 5

child1.show(); // 11 [1,2,1] 5
child2.show(); // 12 [1,2,1] 5

parent.show(); // 1 [1,2,1] 5

child1.show(); // 5 [1,2,1,11,12] 5

child2.show(); // 6 [1,2,1,11,12] 5

这道题目值得神帝,他波及到的知识点很多,例如 this 的指向、原型、原型链、类的继承、数据类型 等。

解析:

  1. parent.show(),能够间接取得所需的值,没啥好说的;
  2. child1.show(),Child的构造函数本来是指向 Child 的,题目显式将 Child 类的原型对象指向了 Parent 类的一个实例,须要留神 Child.prototype 指向的是 Parent 的实例 parent,而不是指向Parent 这个类。
  3. child2.show(),这个也没啥好说的;
  4. parent.show(),parent是一个 Parent 类的实例,Child.prorotype指向的是 Parent 类的另一个实例,两者在堆内存中互不影响,所以上述操作不影响 parent 实例,所以输入后果不变;
  5. child1.show(),child1执行了 change() 办法后,产生了怎么的变动呢?
  6. this.b.push(this.a),因为 this 的动静指向个性,this.b 会指向 Child.prototype 上的 b 数组,this.a 会指向 child1a属性, 所以 Child.prototype.b 变成了[1,2,1,11];
  7. this.a = this.b.length,这条语句中 this.athis.b的指向与上一句统一,故后果为 child1.a 变为4;
  8. this.c.demo = this.a++,因为 child1 本身属性并没有 c 这个属性,所以此处的 this.c 会指向 Child.prototype.cthis.a 值为 4,为原始类型,故赋值操作时会间接赋值,Child.prototype.c.demo 的后果为 4,而this.a 随后自增为5(4 + 1 = 5)。
  9. child2执行了 change() 办法, 而 child2child1均是 Child 类的实例,所以他们的原型链指向同一个原型对象 Child.prototype, 也就是同一个parent 实例,所以 child2.change() 中所有影响到原型对象的语句都会影响 child1 的最终输入后果。
  10. this.b.push(this.a),因为 this 的动静指向个性,this.b 会指向 Child.prototype 上的 b 数组,this.a 会指向 child2a属性, 所以 Child.prototype.b 变成了[1,2,1,11,12];
  11. this.a = this.b.length,这条语句中 this.athis.b的指向与上一句统一,故后果为 child2.a 变为5;
  12. this.c.demo = this.a++,因为 child2 本身属性并没有 c 这个属性,所以此处的 this.c 会指向 Child.prototype.c,故执行后果为Child.prototype.c.demo 的值变为 child2.a 的值 5,而child2.a 最终自增为6(5 + 1 = 6)。

数据类型判断

typeof 能够正确辨认:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,然而对于其余的都会认为是 object,比方 Null、Date 等,所以通过 typeof 来判断数据类型会不精确。然而能够应用 Object.prototype.toString 实现。

function typeOf(obj) {-   let res = Object.prototype.toString.call(obj).split(' ')[1]
-   res = res.substring(0, res.length - 1).toLowerCase()
-   return res
// 更好的写法
+   return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()}
typeOf([])        // 'array'
typeOf({})        // 'object'
typeOf(new Date)  // 'date'

说一下原型链和原型链的继承吧

  • 所有一般的 [[Prototype]] 链最终都会指向内置的 Object.prototype,其蕴含了 JavaScript 中许多通用的性能
  • 为什么能创立“类”,借助一种非凡的属性:所有的函数默认都会领有一个名为 prototype 的共有且不可枚举的属性,它会指向另外一个对象,这个对象通常被称为函数的原型
function Person(name) {this.name = name;}

Person.prototype.constructor = Person
  • 在产生 new 结构函数调用时,会将创立的新对象的 [[Prototype]] 链接到 Person.prototype 指向的对象,这个机制就被称为原型链继承
  • 办法定义在原型上,属性定义在构造函数上
  • 首先要说一下 JS 原型和实例的关系:每个构造函数(constructor)都有一个原型对象(prototype),这个原型对象蕴含一个指向此构造函数的指针属性,通过 new 进行结构函数调用生成的实例,此实例蕴含一个指向原型对象的指针,也就是通过 [[Prototype]] 链接到了这个原型对象
  • 而后说一下 JS 中属性的查找:当咱们试图援用实例对象的某个属性时,是依照这样的形式去查找的,首先查找实例对象上是否有这个属性,如果没有找到,就去结构这个实例对象的构造函数的 prototype 所指向的对象下来查找,如果还找不到,就从这个 prototype 对象所指向的构造函数的 prototype 原型对象下来查找
  • 什么是原型链:这样逐级查找形似一个链条,且通过 [[Prototype]] 属性链接,所以被称为原型链
  • 什么是原型链继承,类比类的继承:当有两个构造函数 A 和 B,将一个构造函数 A 的原型对象的,通过其 [[Prototype]] 属性链接到另外一个 B 构造函数的原型对象时,这个过程被称之为原型继承。

标准答案更正确的解释

什么是原型链?

当对象查找一个属性的时候,如果没有在本身找到,那么就会查找本身的原型,如果原型还没有找到,那么会持续查找原型的原型,直到找到 Object.prototype 的原型时,此时原型为 null,查找进行。
这种通过 通过原型链接的逐级向上的查找链被称为原型链

什么是原型继承?

一个对象能够应用另外一个对象的属性或者办法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样依据原型链的规定,如果查找一个对象属性且在本身不存在时,就会查找另外一个对象,相当于一个对象能够应用另外一个对象的属性和办法了。

Cookie 有哪些字段,作用别离是什么

Cookie 由以下字段组成:

  • Name:cookie 的名称
  • Value:cookie 的值,对于认证 cookie,value 值包含 web 服务器所提供的拜访令牌;
  • Size:cookie 的大小
  • Path:能够拜访此 cookie 的页面门路。比方 domain 是 abc.com,path 是 /test,那么只有/test 门路下的页面能够读取此 cookie。
  • Secure:指定是否应用 HTTPS 平安协定发送 Cookie。应用 HTTPS 平安协定,能够爱护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。该办法也可用于 Web 站点的身份甄别,即在 HTTPS 的连贯建设阶段,浏览器会查看 Web 网站的 SSL 证书的有效性。然而基于兼容性的起因(比方有些网站应用自签订的证书)在检测到 SSL 证书有效时,浏览器并不会立刻终止用户的连贯申请,而是显示平安危险信息,用户仍能够抉择持续拜访该站点。
  • Domain:能够拜访该 cookie 的域名,Cookie 机制并未遵循严格的同源策略,容许一个子域能够设置或获取其父域的 Cookie。当须要实现单点登录计划时,Cookie 的上述个性十分有用,然而也减少了 Cookie 受攻打的危险,比方攻击者能够借此动员会话定置攻打。因此,浏览器禁止在 Domain 属性中设置.org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,以减小攻打产生的范畴。
  • HTTP:该字段蕴含 HTTPOnly 属性,该属性用来设置 cookie 是否通过脚本来拜访,默认为空,即能够通过脚本拜访。在客户端是不能通过 js 代码去设置一个 httpOnly 类型的 cookie 的,这种类型的 cookie 只能通过服务端来设置。该属性用于避免客户端脚本通过document.cookie 属性拜访 Cookie,有助于爱护 Cookie 不被跨站脚本攻打窃取或篡改。然而,HTTPOnly 的利用仍存在局限性,一些浏览器能够阻止客户端脚本对 Cookie 的读操作,但容许写操作;此外大多数浏览器仍容许通过 XMLHTTP 对象读取 HTTP 响应中的 Set-Cookie 头。
  • Expires/Max-size:此 cookie 的超时工夫。若设置其值为一个工夫,那么当达到此工夫后,此 cookie 生效。不设置的话默认值是 Session,意思是 cookie 会和 session 一起生效。当浏览器敞开(不是浏览器标签页,而是整个浏览器) 后,此 cookie 生效。

总结: 服务器端能够应用 Set-Cookie 的响应头部来配置 cookie 信息。一条 cookie 包含了 5 个属性值 expires、domain、path、secure、HttpOnly。其中 expires 指定了 cookie 生效的工夫,domain 是域名、path 是门路,domain 和 path 一起限度了 cookie 可能被哪些 url 拜访。secure 规定了 cookie 只能在确保安全的状况下传输,HttpOnly 规定了这个 cookie 只能被服务器拜访,不能应用 js 脚本拜访。

代码输入后果

function foo(something){this.a = something}

var obj1 = {foo: foo}

var obj2 = {}

obj1.foo(2); 
console.log(obj1.a); // 2

obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3

var bar = new obj1.foo(4)
console.log(obj1.a); // 2
console.log(bar.a); // 4

输入后果:2 3 2 4

解析:

  1. 首先执行 obj1.foo(2); 会在 obj 中增加 a 属性,其值为 2。之后执行 obj1.a,a 是右 obj1 调用的,所以 this 指向 obj,打印出 2;
  2. 执行 obj1.foo.call(obj2, 3) 时,会将 foo 的 this 指向 obj2,前面就和下面一样了,所以会打印出 3;
  3. obj1.a 会打印出 2;
  4. 最初就是考查 this 绑定的优先级了,new 绑定是比隐式绑定优先级高,所以会输入 4。

正文完
 0