目录
js中new一个对象的过程
使用new关键字调用函数(new ClassA(…))的具体步骤:
1. 创建空对象;
var obj = {};
2. 设置新对象的constructor属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的prototype对象;
obj.__proto__ = ClassA.prototype;
3. 使用新对象调用函数,函数中的this被指向新实例对象:
ClassA.call(obj);  //{}.构造函数();
4. 将初始化完毕的新对象地址,保存到等号左边的变量中
什么是闭包?闭包的优缺点
答:闭包是将外部作用域中的局部变量封闭起来的函数对象。被封闭起来的变量与封闭它的函数对象有相同的生命周期。
优点:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。
缺点:
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
什么是盒子模型?CSS-标准盒模型和怪异盒模型的区别?哪个css可以改变盒子模型?
css盒子模型 又称为框模型(Box Model),包含了元素内容(content)、内边距(padding)、边框(border)、外边距(margin)几个要素。
标准盒模型
怪异盒模型
区别:当不对doctype进行定义时,会触发怪异模式。
- 在标准模式下,一个块的总宽度= width + margin(左右) + padding(左右) + border(左右)
- 在怪异模式下,一个块的总宽度= width + margin(左右)(即width已经包含了padding和border值)
display取值,元素默认是什么,内联元素块级元素可变元素有哪些?img是什么?
img是内联元素
display CSS属性指定用于元素的呈现框的类型。在 HTML 中,默认的 display 属性取决于 HTML 规范所描述的行为或浏览器/用户的默认样式表。
默认值是inline
display取值如下:
none 此元素不会被显示。
block 此元素将显示为块级元素,此元素前后会带有换行符。
inline 默认。此元素会被显示为内联元素,元素前后没有换行符。
inline-block 行内块元素。(CSS2.1 新增的值)
list-item 此元素会作为列表显示。
run-in 此元素会根据上下文作为块级元素或内联元素显示。
compact CSS 中有值 compact,不过由于缺乏广泛支持,已经从 CSS2.1 中删除。
marker CSS 中有值 marker,不过由于缺乏广泛支持,已经从 CSS2.1 中删除。
table 此元素会作为块级表格来显示(类似 <table>),表格前后带有换行符。
inline-table 此元素会作为内联表格来显示(类似 <table>),表格前后没有换行符。
table-row-group 此元素会作为一个或多个行的分组来显示(类似 <tbody>)。
table-header-group 此元素会作为一个或多个行的分组来显示(类似 <thead>)。
table-footer-group 此元素会作为一个或多个行的分组来显示(类似 <tfoot>)。
table-row 此元素会作为一个表格行显示(类似 <tr>)。
table-column-group 此元素会作为一个或多个列的分组来显示(类似 <colgroup>)。
table-column 此元素会作为一个单元格列显示(类似 <col>)
table-cell 此元素会作为一个表格单元格显示(类似 <td> 和 <th>)
table-caption 此元素会作为一个表格标题显示(类似 <caption>)
inherit 规定应该从父元素继承 display 属性的值。
float:left情况下是怎样的,此时如果超出了宽度范围
该元素从网页的正常流动中移除,尽管仍然保持部分的流动性
css3的transform
CSS transform 属性允许你修改CSS视觉格式模型的坐标空间。使用它,元素可以被转换(translate)、旋转(rotate)、缩放(scale)、倾斜(skew)。
输入一个URL后会发生什么?
总体来说分为以下几个过程:
- DNS解析
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
tcp 协议,http协议
hit-alibaba.github.io/interview/b…
tcp协议即是传输控制协议,
http协议:
HTTP构建于TCP/IP协议之上,默认端口号是80 HTTP是无连接无状态的
写一个继承函数
function Parent() {
this.property = true
}
Parent.prototype.getParentValue = function () {
return this.property
}
function Child() {
this.Childproperty = false
}
Child.prototype = new Parent()
Child.prototype.getChildValue = function () {
return this.Childproperty
}
var instance = new Child()
console.log(instance.getParentValue()) // true
写一个快速排序
"快速排序"的思想很简单,整个排序过程只需要三步:
(1)在数据集之中,选择一个元素作为"基准"(pivot)。
(2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。
(3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
function quickSort(arr) {
if(arr.length < 2) {
return arr;
} else {
const pivot = arr[0]; // 基准值
const pivotArr = []; // 一样大的放中间
const lowArr= []; // 小的放左边
const hightArr = []; // 大的放右边
arr.forEach(current => {
if(current === pivot) pivotArr.push(current);
else if(current > pivot) hightArr.push(current);
else lowArr.push(current);
})
return quickSort(lowArr).concat(pivotArr).concat(quickSort(hightArr));
}
}
写一个js判断全等的方法
//利用JSON.stringify,将两个对象转化为字符串。
字符串相等的话,说明两个对象全等。
let a = {a:0,b:1,c:2};
let b = {a:0,b:1,c:2};
let c = {a:1,b:1,c:2};
let x = JSON.stringify(a) == JSON.stringify(b);
let y = JSON.stringify(a) == JSON.stringify(c);
console.log(x);
console.log(y);
遍历对象的方法
var obj={a:'A',b:'B',c:'C'};
1. for ... in 循环
for(key in obj){
console.log(key);// a,b,c
}
2. Object.keys()
Object.keys(obj);//["a", "b", "c"]
防抖和节流
- 防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
思路
每次触发事件时都取消之前的延时调用方法
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
- 节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
思路
每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
问题
为什么要 fn.apply(this, arguments);而不是这样 fn()
答:加上 apply 确保 在 sayHi 函数里的 this 指向的是 input对象(不然就指向 window 了,不是我们想要的)。 这里的箭头函数依旧是指向 input 对象。
https怎么保证安全的? 什么情况下会碰到跨域问题?有哪些解决方法?
- 跨域问题是这是浏览器为了安全实施的同源策略导致的,同源策略限制了来自不同源的document、脚本,同源的意思就是两个URL的域名、协议、端口要完全相同。
- script标签jsonp跨域、nginx反向代理、node.js中间件代理跨域、后端在头部信息设置安全域名、后端在服务器上设置cors。
如何判断一个变量是对象还是数组?
function isObjArr(value){
if (Object.prototype.toString.call(value) === "[object Array]") {
console.log('value是数组');
}else if(Object.prototype.toString.call(value)==='[object Object]'){//这个方法兼容性好一点
console.log('value是对象');
}else{
console.log('value不是数组也不是对象')
}
}
//ps:千万不能使用typeof来判断对象和数组,因为这两种类型都会返回"object"。
事件循环机制
html事件循环:
一个浏览器环境,只能有一个事件循环,而一个事件循环可以多个任务队列(task queue),每个任务都有一个任务源(task source)。
相同任务源的任务,只能放到一个任务队列中。
不同任务源的任务,可以放到不同任务队列中。
EcmaScript规范中指出:
任务队列(Job queue)是一个先进先出的队列,每一个任务队列是有名字的,至于有多少个任务队列,取决于实现。每一个实现至少应该包含以上两个任务队列。
结论:EcmaScript的Job queue与HTML的Task queue有异曲同工之妙。它们都可以有好几个,多个任务队列之间的顺序都是不保证的。
例子:
setImmediate(function(){
console.log(1);
},0);
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);
结果:3 4 6 8 7 5 2 1
事件注册顺序如下:
setImmediate - setTimeout - promise.then - process.nextTick
优先级关系:
process.nextTick > promise.then > setTimeout > setImmediate
V8实现中,两个队列各包含不同的任务:
macrotasks(宏任务): script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks(微任务): process.nextTick, Promises, Object.observe, MutationObserver
执行过程如下:
js引擎首先从macrotask queue中取出第一个任务,执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行;然后再从macrotask queue中取下一个,执行完毕后,再次将microtask queue中的全部取出; 循环往复,直到两个queue中的任务都取完。
setTimeout会默认延迟4毫秒(ms)。
问题:
process.nextTick也会放入microtask quque,为什么优先级比promise.then高呢?
答:process.nextTick 永远大于promise.then,原因其实很简单。
在Node中,_tickCallback在每一次执行完TaskQueue中的一个任务后被调用,而这个_tickCallback中实质上干了两件事:
1. nextTickQueue中所有任务执行掉(长度最大1e4,Node版本v6.9.1)
2. 第一步执行完后执行_runMicrotasks函数,执行microtask中的部分(promise.then注册的回调)
所以很明显process.nextTick > promise.then
深拷贝和浅拷贝
区别
-
浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用
-
深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
深拷贝的要求程度
我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素,还是递归拷贝所有层级的对象属性和数组元素?
怎么检验深拷贝成功
改变任意一个新对象/数组中的属性/元素, 都不改变原对象/数组
只做第一层深拷贝
深拷贝数组(只拷贝第一级数组元素)
- 直接遍历
var arr = [1,2,3,4];
function copy(arg){
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr.push(arr[i]);
}
return newArr;
}
var newArry = copy(arr);
console.log(newArry);
newArry[0] = 10;
console.log(newArry); // [10,2,3,4]
console.log(arr) // [1,2,3,4]
- slice()
var arr = [1,2,3,4]
var copyArr = arr.slice();
copyArr[0] = 10;
console.log(copyArr); // [10,2,3,4]
console.log(arr); // [1,2,3,4]
// slice() 方法返回一个从已有的数组中截取一部分元素片段组成的新数组(不改变原来的数组!)
用法:array.slice(start,end) start表示是起始元素的下标, end表示的是终止元素的下标
当slice()不带任何参数的时候,默认返回一个长度和原数组相同的新数组
- concat()
var arr = [1,2,3,4]
var copyArr = arr.concat();
copyArr[0] = 10;
console.log(copyArr); // [10,2,3,4]
console.log(arr); // [1,2,3,4]
//concat() 方法用于连接两个或多个数组。( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。)
用法:array.concat(array1,array2,......,arrayN)
因为我们上面调用concat的时候没有带上参数,所以var copyArray = array.concat();实际上相当于var copyArray = array.concat([]);
也即把返回数组和一个空数组合并后返回
深拷贝对象
- 直接遍历
var obj = {
name: "张三",
job: "学生"
}
function copy (obj) {
let newobj = {}
for(let item in obj) {
newobj[item] = obj[item];
}
return newobj;
}
var copyobj = copy(obj)
copyobj.name = "李四"
console.log(copyobj) // {name: '李四', job:: '学生'}
console.log(obj) // {name: '张三', job:: '学生'}
- ES6的Object.assign
var obj = {
name: '张三',
job: '学生'
}
var copyobj = Object.assign({},obj)
copyobj.name = '李四'
console.log(copyobj) // {name: '李四', job:: '学生'}
console.log(obj) // {name: '张三', job:: '学生'}
Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的target
用法: Object.assign(target, source1, source2); 所以 copyObj = Object.assign({}, obj); 这段代码将会把obj中的一级属性都拷贝到 {}中,然后将其返回赋给copyObj
- ES6扩展运算符:
var obj = {
name: '张三',
job: '学生'
}
var copyobj = {...obj}
copyobj.name = '李四'
console.log(copyobj)
console.log(obj)
扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中
ajax请求
// 1.XMLHttpRequest对象用于在后台与服务器交换数据
var xhr = new XMLHttpRequest();
// 2.
xhr.open('GET', url, true);
//3.发送请求
xhr.send();
//4.接收返回
//客户端和服务器端有交互的时候会调用onreadystatechange
xhr.onreadystatechange = function() {
// readyState == 4说明请求已完成
if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
// 从服务器获得数据
fn.call(this, xhr.responseText);
}
};