前端知识点

目录

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)几个要素。

标准盒模型
avatar
怪异盒模型
avatar
区别:当不对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"]

防抖和节流

  1. 防抖

触发高频事件后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)); // 防抖
  1. 节流

高频事件触发,但在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

深拷贝和浅拷贝

区别

  1. 浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

  2. 深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

深拷贝的要求程度

我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素,还是递归拷贝所有层级的对象属性和数组元素?

怎么检验深拷贝成功
改变任意一个新对象/数组中的属性/元素, 都不改变原对象/数组

只做第一层深拷贝

深拷贝数组(只拷贝第一级数组元素)

  1. 直接遍历
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]
  1. 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()不带任何参数的时候,默认返回一个长度和原数组相同的新数组

  1. 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([]);
也即把返回数组和一个空数组合并后返回

深拷贝对象

  1. 直接遍历
 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:: '学生'}

  1. 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
  1. 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);  
    }
};
posted @ 2019-08-03 16:36  dobeco  阅读(417)  评论(0编辑  收藏  举报