乐趣区

关于前端:前端高频面试题五附答案

如何取得对象非原型链上的属性?

应用后 hasOwnProperty() 办法来判断属性是否属于原型链的属性:

function iterate(obj){var res=[];
   for(var key in obj){if(obj.hasOwnProperty(key))
           res.push(key+':'+obj[key]);
   }
   return res;
} 

一个 tcp 连贯能发几个 http 申请?

如果是 HTTP 1.0 版本协定,个别状况下,不反对长连贯,因而在每次申请发送结束之后,TCP 连贯即会断开,因而一个 TCP 发送一个 HTTP 申请,然而有一种状况能够将一条 TCP 连贯放弃在沉闷状态,那就是通过 Connection 和 Keep-Alive 首部,在申请头带上 Connection: Keep-Alive,并且能够通过 Keep-Alive 通用首部中指定的,用逗号分隔的选项调节 keep-alive 的行为,如果客户端和服务端都反对,那么其实也能够发送多条,不过此形式也有限度,能够关注《HTTP 权威指南》4.5.5 节对于 Keep-Alive 连贯的限度和规定。

而如果是 HTTP 1.1 版本协定,反对了长连贯,因而只有 TCP 连接不断开,便能够始终发送 HTTP 申请,继续一直,没有下限;同样,如果是 HTTP 2.0 版本协定,反对多用复用,一个 TCP 连贯是能够并发多个 HTTP 申请的,同样也是反对长连贯,因而只有一直开 TCP 的连贯,HTTP 申请数也是能够没有下限地继续发送

继承

原型链继承

function Animal() {this.colors = ['black', 'white']
}
Animal.prototype.getColor = function() {return this.colors}
function Dog() {}
Dog.prototype =  new Animal()

let dog1 = new Dog()
dog1.colors.push('brown')
let dog2 = new Dog()
console.log(dog2.colors)  // ['black', 'white', 'brown']

原型链继承存在的问题:

  • 问题 1:原型中蕴含的援用类型属性将被所有实例共享;
  • 问题 2:子类在实例化的时候不能给父类构造函数传参;

借用构造函数实现继承

function Animal(name) {
    this.name = name
    this.getName = function() {return this.name}
}
function Dog(name) {Animal.call(this, name)
}
Dog.prototype =  new Animal()

借用构造函数实现继承解决了原型链继承的 2 个问题:援用类型共享问题以及传参问题。然而因为办法必须定义在构造函数中,所以会导致每次创立子类实例都会创立一遍办法。

组合继承

组合继承联合了原型链和盗用构造函数,将两者的长处集中了起来。根本的思路是应用原型链继承原型上的属性和办法,而通过盗用构造函数继承实例属性。这样既能够把办法定义在原型上以实现重用,又能够让每个实例都有本人的属性。

function Animal(name) {
    this.name = name
    this.colors = ['black', 'white']
}
Animal.prototype.getName = function() {return this.name}
function Dog(name, age) {Animal.call(this, name)
    this.age = age
}
Dog.prototype =  new Animal()
Dog.prototype.constructor = Dog

let dog1 = new Dog('奶昔', 2)
dog1.colors.push('brown')
let dog2 = new Dog('哈赤', 1)
console.log(dog2) 
// {name: "哈赤", colors: ["black", "white"], age: 1 }

寄生式组合继承

组合继承曾经绝对欠缺了,但还是存在问题,它的问题就是调用了 2 次父类构造函数,第一次是在 new Animal(),第二次是在 Animal.call() 这里。

所以解决方案就是不间接调用父类构造函数给子类原型赋值,而是通过创立空函数 F 获取父类原型的正本。

寄生式组合继承写法上和组合继承根本相似,区别是如下这里:

- Dog.prototype =  new Animal()
- Dog.prototype.constructor = Dog

+ function F() {}
+ F.prototype = Animal.prototype
+ let f = new F()
+ f.constructor = Dog
+ Dog.prototype = f

略微封装下下面增加的代码后:

function object(o) {function F() {}
    F.prototype = o
    return new F()}
function inheritPrototype(child, parent) {let prototype = object(parent.prototype)
    prototype.constructor = child
    child.prototype = prototype
}
inheritPrototype(Dog, Animal)

如果你厌弃下面的代码太多了,还能够基于组合继承的代码改成最简略的寄生式组合继承:

- Dog.prototype =  new Animal()
- Dog.prototype.constructor = Dog

+ Dog.prototype =  Object.create(Animal.prototype)
+ Dog.prototype.constructor = Dog

class 实现继承

class Animal {constructor(name) {this.name = name} 
    getName() {return this.name}
}
class Dog extends Animal {constructor(name, age) {super(name)
        this.age = age
    }
}

对 rest 参数的了解

扩大运算符被用在函数形参上时,它还能够把一个拆散的参数序列整合成一个数组

function mutiple(...args) {
  let result = 1;
  for (var val of args) {result *= val;}
  return result;
}
mutiple(1, 2, 3, 4) // 24

这里,传入 mutiple 的是四个拆散的参数,然而如果在 mutiple 函数里尝试输入 args 的值,会发现它是一个数组:

function mutiple(...args) {console.log(args)
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]

这就是 … rest 运算符的又一层威力了,它能够把函数的多个入参收敛进一个数组里。这一点 常常用于获取函数的多余参数,或者像下面这样解决函数参数个数不确定的状况。

怎么加事件监听,两种

onclick 和 addEventListener

伪元素和伪类的区别和作用?

  • 伪元素:在内容元素的前后插入额定的元素或款式,然而这些元素实际上并不在文档中生成。它们只在内部显示可见,但不会在文档的源代码中找到它们,因而,称为“伪”元素。例如:
p::before {content:"第一章:";}
p::after {content:"Hot!";}
p::first-line {background:red;}
p::first-letter {font-size:30px;}
  • 伪类:将非凡的成果增加到特定选择器上。它是已有元素上增加类别的,不会产生新的元素。例如:
a:hover {color: #FF00FF}
p:first-child {color: red}

总结: 伪类是通过在元素选择器上加⼊伪类扭转元素状态,⽽伪元素通过对元素的操作进⾏对元素的扭转。

同步和异步的区别

  • 同步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,那么这个过程会始终期待上来,直到音讯返回为止再持续向下执行。
  • 异步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,这个时候过程会持续往下执行,不会阻塞期待音讯的返回,当音讯返回时零碎再告诉过程进行解决。

大数相加

题目形容: 实现一个 add 办法实现两个大数相加

let a = "9007199254740991";
let b = "1234567899999999999";

function add(a ,b){//...}

实现代码如下:

function add(a ,b){
   // 取两个数字的最大长度
   let maxLength = Math.max(a.length, b.length);
   // 用 0 去补齐长度
   a = a.padStart(maxLength , 0);//"0009007199254740991"
   b = b.padStart(maxLength , 0);//"1234567899999999999"
   // 定义加法过程中须要用到的变量
   let t = 0;
   let f = 0;   //"进位"
   let sum = "";
   for(let i=maxLength-1 ; i>=0 ; i--){t = parseInt(a[i]) + parseInt(b[i]) + f;
      f = Math.floor(t/10);
      sum = t%10 + sum;
   }
   if(f!==0){sum = '' + f + sum;}
   return sum;
}

对 sticky 定位的了解

sticky 英文字面意思是粘贴,所以能够把它称之为粘性定位。语法:position: sticky; 基于用户的滚动地位来定位。

粘性定位的元素是依赖于用户的滚动,在 position:relativeposition:fixed 定位之间切换。它的行为就像 position:relative; 而当页面滚动超出指标区域时,它的体现就像 position:fixed;,它会固定在指标地位。元素定位体现为在逾越特定阈值前为绝对定位,之后为固定定位。这个特定阈值指的是 top, right, bottom 或 left 之一,换言之,指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位失效。否则其行为与绝对定位雷同。

对类数组对象的了解,如何转化为数组

一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,函数参数也能够被看作是类数组对象,因为它含有 length 属性值,代表可接管的参数个数。

常见的类数组转换为数组的办法有这样几种:

  • 通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
  • 通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
  • 通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
  • 通过 Array.from 办法来实现转换
Array.from(arrayLike);

数组的遍历办法有哪些

办法 是否扭转原数组 特点
forEach() 数组办法,不扭转原数组,没有返回值
map() 数组办法,不扭转原数组,有返回值,可链式调用
filter() 数组办法,过滤数组,返回蕴含符合条件的元素的数组,可链式调用
for…of for…of 遍历具备 Iterator 迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历一般的 obj 对象,将异步循环变成同步循环
every() 和 some() 数组办法,some()只有有一个是 true,便返回 true;而 every()只有有一个是 false,便返回 false.
find() 和 findIndex() 数组办法,find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值
reduce() 和 reduceRight() 数组办法,reduce()对数组正序操作;reduceRight()对数组逆序操作

OPTIONS 申请办法及应用场景

OPTIONS 是除了 GET 和 POST 之外的其中一种 HTTP 申请办法。

OPTIONS 办法是用于申请取得由 Request-URI 标识的资源在申请 / 响应的通信过程中能够应用的性能选项。通过这个办法,客户端能够 在采取具体资源申请之前,决定对该资源采取何种必要措施,或者理解服务器的性能。该申请办法的响应不能缓存。

OPTIONS 申请办法的 主要用途 有两个:

  • 获取服务器反对的所有 HTTP 申请办法;
  • 用来查看拜访权限。例如:在进行 CORS 跨域资源共享时,对于简单申请,就是应用 OPTIONS 办法发送嗅探申请,以判断是否有对指定资源的拜访权限。

什么是 CSRF 攻打?

(1)概念

CSRF 攻打指的是 跨站申请伪造攻打,攻击者诱导用户进入一个第三方网站,而后该网站向被攻打网站发送跨站申请。如果用户在被攻打网站中保留了登录状态,那么攻击者就能够利用这个登录状态,绕过后盾的用户验证,假冒用户向服务器执行一些操作。

CSRF 攻打的 实质是利用 cookie 会在同源申请中携带发送给服务器的特点,以此来实现用户的假冒。

(2)攻打类型

常见的 CSRF 攻打有三种:

  • GET 类型的 CSRF 攻打,比方在网站中的一个 img 标签里构建一个申请,当用户关上这个网站的时候就会主动发动提交。
  • POST 类型的 CSRF 攻打,比方构建一个表单,而后暗藏它,当用户进入页面时,主动提交这个表单。
  • 链接类型的 CSRF 攻打,比方在 a 标签的 href 属性里构建一个申请,而后诱导用户去点击。

对 Flex 布局的了解及其应用场景

Flex 是 FlexibleBox 的缩写,意为 ” 弹性布局 ”,用来为盒状模型提供最大的灵活性。任何一个容器都能够指定为 Flex 布局。行内元素也能够应用 Flex 布局。留神,设为 Flex 布局当前,子元素的 float、clear 和 vertical-align 属性将生效。采纳 Flex 布局的元素,称为 Flex 容器(flex container),简称 ” 容器 ”。它的所有子元素主动成为容器成员,称为 Flex 我的项目(flex item),简称 ” 我的项目 ”。容器默认存在两根轴:程度的主轴(main axis)和垂直的穿插轴(cross axis),我的项目默认沿程度主轴排列。

以下 6 个属性设置在 容器上

  • flex-direction 属性决定主轴的方向(即我的项目的排列方向)。
  • flex-wrap 属性定义,如果一条轴线排不下,如何换行。
  • flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写模式,默认值为 row nowrap。
  • justify-content 属性定义了我的项目在主轴上的对齐形式。
  • align-items 属性定义我的项目在穿插轴上如何对齐。
  • align-content 属性定义了多根轴线的对齐形式。如果我的项目只有一根轴线,该属性不起作用。

以下 6 个属性设置在 我的项目上

  • order 属性定义我的项目的排列程序。数值越小,排列越靠前,默认为 0。
  • flex-grow 属性定义我的项目的放大比例,默认为 0,即如果存在残余空间,也不放大。
  • flex-shrink 属性定义了我的项目的放大比例,默认为 1,即如果空间有余,该我的项目将放大。
  • flex-basis 属性定义了在调配多余空间之前,我的项目占据的主轴空间。浏览器依据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即我的项目的原本大小。
  • flex 属性是 flex-grow,flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。
  • align-self 属性容许单个我的项目有与其余我的项目不一样的对齐形式,可笼罩 align-items 属性。默认值为 auto,示意继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。

简略来说: flex 布局是 CSS3 新增的一种布局形式,能够通过将一个元素的 display 属性值设置为 flex 从而使它成为一个 flex 容器,它的所有子元素都会成为它的我的项目。一个容器默认有两条轴:一个是程度的主轴,一个是与主轴垂直的穿插轴。能够应用 flex-direction 来指定主轴的方向。能够应用 justify-content 来指定元素在主轴上的排列形式,应用 align-items 来指定元素在穿插轴上的排列形式。还能够应用 flex-wrap 来规定当一行排列不下时的换行形式。对于容器中的我的项目,能够应用 order 属性来指定我的项目的排列程序,还能够应用 flex-grow 来指定当排列空间有残余的时候,我的项目的放大比例,还能够应用 flex-shrink 来指定当排列空间有余时,我的项目的放大比例。

代码输入后果

function SuperType(){this.property = true;}

SuperType.prototype.getSuperValue = function(){return this.property;};

function SubType(){this.subproperty = false;}

SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){return this.subproperty;};

var instance = new SubType();
console.log(instance.getSuperValue());

输入后果:true

实际上,这段代码就是在实现原型链继承,SubType 继承了 SuperType,实质是重写了 SubType 的原型对象,代之以一个新类型的实例。SubType 的原型被重写了,所以 instance.constructor 指向的是 SuperType。具体如下:

字符串模板

function render(template, data) {const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
    if (reg.test(template)) { // 判断模板里是否有模板字符串
        const name = reg.exec(template)[1]; // 查找以后模板里第一个模板字符串的字段
        template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
        return render(template, data); // 递归的渲染并返回渲染后的构造
    }
    return template; // 如果模板没有模板字符串间接返回
}

测试:

let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let person = {
    name: '布兰',
    age: 12
}
render(template, person); // 我是布兰,年龄 12,性别 undefined

如果 new 一个箭头函数的会怎么样

箭头函数是 ES6 中的提出来的,它没有 prototype,也没有本人的 this 指向,更不能够应用 arguments 参数,所以不能 New 一个箭头函数。

new 操作符的实现步骤如下:

  1. 创立一个对象
  2. 将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的 prototype 属性)
  3. 指向构造函数中的代码,构造函数中的 this 指向该对象(也就是为这个对象增加属性和办法)
  4. 返回新的对象

所以,下面的第二、三步,箭头函数都是没有方法执行的。

map 和 Object 的区别

Map Object
意外的键 Map 默认状况不蕴含任何键,只蕴含显式插入的键。 Object 有一个原型, 原型链上的键名有可能和本人在对象上的设置的键名产生抵触。
键的类型 Map 的键能够是任意值,包含函数、对象或任意根本类型。 Object 的键必须是 String 或是 Symbol。
键的程序 Map 中的 key 是有序的。因而,当迭代的时候,Map 对象以插入的程序返回键值。 Object 的键是无序的
Size Map 的键值对个数能够轻易地通过 size 属性获取 Object 的键值对个数只能手动计算
迭代 Map 是 iterable 的,所以能够间接被迭代。 迭代 Object 须要以某种形式获取它的键而后能力迭代。
性能 在频繁增删键值对的场景下体现更好。 在频繁增加和删除键值对的场景下未作出优化。

forEach 和 map 办法有什么区别

这办法都是用来遍历数组的,两者区别如下:

  • forEach()办法会针对每一个元素执行提供的函数,对数据的操作会扭转原数组,该办法没有返回值;
  • map()办法不会扭转原数组的值,返回一个新数组,新数组中的值为原数组调用函数解决之后的值;

数组去重

ES5 实现:

function unique(arr) {var res = arr.filter(function(item, index, array) {return array.indexOf(item) === index
    })
    return res
}

ES6 实现:

var unique = arr => [...new Set(arr)]
退出移动版