前端面试题合集

面临找工做了,准备收集一些面试题目,供本身以及你们一块儿学习吧~javascript

面试题目来源于网络,做者只是整理~css


1、问题一: 关于 const 和 let 声明的变量不在 window 上的疑问?

关于 const 和 let 声明的变量不在 window 上的疑问?
复制代码

回答区:html

在ES5中规定顶层对象的属性和全局变量是等价的。经过 var 声明的变量或者函数既是全局属性又是顶层对象的属性。前端

var a = 10;
var f = function(){}

console.log(window.a)  // 10
console.log(window.f)  // ƒ (){}
复制代码

ES6中规定,var 声明的全局函数和全局变量, 依旧是顶层对象的属性,可是经过let或者const声明的全局函数和全局变量,却再也不是顶层对象的属性。vue

const b = 20;
let ff = function(){};

console.log(window.b)  // undefined
console.log(window.ff)  // undefined
复制代码

经过上图也能够看到,在全局做用域中,用 let 和 const 声明的全局变量并无在全局对象中,只是一个块级做用域(Script)中html5

const b = 20;
let ff = function(){};

console.log(b)  
console.log(ff) 
复制代码

2、问题二: ['1', '2', '3'].map(parseInt)

['1', '2', '3'].map(parseInt) 解析
复制代码

回答区java

map函数的格式:node

array.map( function( currentValue, index, arr ),  thisValue )

第一个参数:  必须。函数,数组中的每一个元素都会执行这个函数
            currentValue	必须。当前元素的值
            index	        可选。当前元素的索引值
            arr	                可选。当前元素属于的数组对象
第二个参数: 可选。对象做为该执行回调时使用,传递给函数,用做 "this" 的值。
           若是省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。
复制代码

上题能够理解为执行下面的三条:react

parseInt('1', 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt('2', 1) //基数为1(1进制)表示的数中,最大值小于2,因此没法解析,返回NaN
parseInt('3', 2) //基数为2(2进制)表示的数中,最大值小于3,因此没法解析,返回NaN
复制代码

parseInt()详解css3

主要解释下面的计算方法:

parseInt("10");			//返回 10
parseInt("19",10);		//返回 19 (10+9)
parseInt("11",2);		//返回 3 (2+1)
parseInt("17",8);		//返回 15 (8+7)
parseInt("1f",16);		//返回 31 (16+15)
parseInt("010");		//未定:返回 10 或 8
复制代码
parseInt(string,radix); 
复制代码

规则以下:

其中的基数  radix.(不表明着进制) 不少人都误觉得它表明着要转换的进制数。
string要转换的字符串,string 以 "0x" 开头,parseInt() 会把 string 的其他部分解析为十六进制的整数。

若是 string 以 0 开头,那么会把其后的字符解析为八进制或十六进制的数字。若是 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。
复制代码

知道上面的规则后:

parseInt("10");	  

// 默认radix为10,string为数字开头,则解析为10进制的整数,则parseInt("10")=1*10^1+0*10^0=10;不变,其中10为基数]

parseInt('11',2);
// radix 为2, string为数字开头,则 parseInt('11',2) =1*2^1+1*2^0=3; 其中2为基数

parseInt('1f',16);
// string为1f,解析为16进制。radix为16,则=1*16^1+15*16^0=31;其中16为基数,f=15;

parseInt("17",6)=1,parseInt('17'9)=16;
//当解析17时,1属于6进制范围,7不属于6进制范围,当string的数字小于radix时(7<6),它会只解析到它的上一位,
// parseInt('17',6) = parseInt('1',6) = 1;
复制代码

例题:

var a=["1", "2", "3", "4", "5"]; a.map(parseInt); 


parseInt('1',0) // 1
parseInt('2',1) // nan
parseInt('3',2) // 由于2进制范围为(0-2) 3不在2进制范围,因此NaN
parseInt('4',3) // 由于3进制范围为(0-2) 4不在3进制范围,因此NaN
parseInt('5',4) // NaN

复制代码

3、问题三: 说说你对 Set、Map、WeakSet 和 WeakMap 的认识

说说你对 Set、Map、WeakSet 和 WeakMap 的认识
复制代码

Set

Set能够理解成是一个数据结构,是一种集合类型的数据结构,相似与数组。内部的成员都是惟一的,不重复,无序。

new Set([iterable])
复制代码

举例子认识一下:

var s = new Set();
[1,2,3,4,5,4,3,2].forEach((x) => {
    s.add(x)   
})


// 数组去重
var arr = [1,2,3,4,5, 4,5,6,7,1];
[...new Set(arr)] // [1, 2, 3, 4, 5, 6, 7]
复制代码

向set中添加内容的时候不会进行类型转换,好比 5 和 “5” 会存为两个值。其内部使用了相似与===的机制,可是由不同,由于 NaN === NaN 返回false,可是set中却会只存一次,认为是相同的。

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

let set1 = new Set()
set1.add(5)
set1.add('5')
console.log([...set1])	// [5, "5"]
复制代码

Set的属性和方法

(1)Set的属性

  • constructor: 构造函数
  • size:元素数量
let set = new Set([1, 2, 3, 2, 1])

console.log(set.length)	// undefined
console.log(set.size)	// 3
复制代码

(2)Set的实例方法

  • add(value):新增,至关于 array里的push
  • delete(value):存在即删除集合中value
  • has(value):判断集合中是否存在 value
  • clear():清空集合

set的基本操做:

let set = new Set()
set.add(1).add(2).add(1)

set.has(1)	// true
set.has(3)	// false
set.delete(1)	
set.has(1)	// false
复制代码

Array.from 方法能够将 Set 结构转为数组

var set = new Set([1,2,3,4,5]);

var array = Array.form(set);
console.log(array);

var arr = [...set];
复制代码

最重要的就是遍历方法了:

  • keys():返回一个包含集合中全部键的迭代器
  • values():返回一个包含集合中全部值得迭代器
  • entries():返回一个包含Set对象中全部元素得键值对迭代器
  • forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操做,若是提供了 thisArg 参数,回调中的this会是这个参数,没有返回值
var set = new Set([1,2,3,4]);
console.log(set.keys());

for (let i of set.keys()) {
    console.log(i);
}
// SetIterator {1, 2, 3, 4}
// 1
// 2
// 3
// 4

for (let i of set.values()) {
    console.log(i);
}
// 1
// 2
// 3
// 4

for (let i of set.entries()) {
    console.log(i);
}
// 返回键值对
// [1, 1]
// [2, 2]
// [3, 3]
// [4, 4]

set.forEach((value, key) => {
    console.log(key + ' : ' + value)
})	
// 1 : 1
// 2 : 2
// 3 : 3
// 4 : 4
复制代码

set默认使用的是values以后的迭代器。

  • 使用map、filter
var set = new Set([1,2,3,4]);

[...set].map((item) => {
    return item * 2;
})
// 2,4,6,8

[...set].filter((item) => {
    return item >= 4;
})
// 4
复制代码

所以,Set 很容易实现交集(Intersect)、并集(Union)、差集(Difference)

let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])

let intersect = new Set([...set1].filter(value => set2.has(value)))
let union = new Set([...set1, ...set2])
let difference = new Set([...set1].filter(value => !set2.has(value)))

console.log(intersect)	// Set {2, 3}
console.log(union)		// Set {1, 2, 3, 4}
console.log(difference)	// Set {1}
复制代码

WeakSet

WeakSet容许你存放弱引用的对象。

WeakSet 与 Set的区别:

一、WeakSet 只能储存对象引用,不能存放值,而 Set 对象均可以
二、WeakSet 存放的都是弱引用的对象,垃圾回收机制不会由于这些弱引用而放弃对该对象的回收。
三、WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行,运行先后成员个数可能不一致,遍历结束以后,有的成员可能取不到了(被垃圾回收了),WeakSet 对象是没法被遍历的(ES6 规定 WeakSet 不可遍历),也没有办法拿到它包含的全部元素
复制代码
var set = new WeakSet([[1,2], [3,4]]);

console.log(set)
复制代码

  • add(value):在WeakSet 对象中添加一个元素value
  • has(value):判断 WeakSet 对象中是否包含value
  • delete(value):删除元素 value
var ws = new WeakSet()
var obj = {}
var foo = {}

ws.add(window)
ws.add(obj)

ws.has(window)	// true
ws.has(foo)	// false

ws.delete(window)	// true
ws.has(window)	// false
复制代码

Map

Map是一种数据结构,存储键值对。

Map构造函数的参数

var map = new Map([['a', 1], ['b', 2]]);

// Map(2) {"a" => 1, "b" => 2}

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1
// Map(2) {"foo" => 1, "bar" => 2}
复制代码

任何具备 Iterator 接口、且每一个成员都是一个双元素的数组的数据结构均可以看成Map构造函数的参数。

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123
复制代码

Map 的属性及方法

(1)属性

  • constructor
  • size
const map = new Map([
  ['name', 'An'],
  ['des', 'JS']
]);

map.size // 2
复制代码

(2)方法

  • set(key, value):向字典中添加新元素
  • get(key):经过键查找特定的数值并返回
  • has(key):判断字典中是否存在键key
  • delete(key):经过键 key 从字典中移除对应的数据
  • clear():将这个字典中的全部元素删除

遍历方法

  • Keys():将字典中包含的全部键名以迭代器形式返回
  • values():将字典中包含的全部数值以迭代器形式返回
  • entries():返回全部成员的迭代器
  • forEach():遍历字典的全部成员
const map = new Map([
            ['name', 'An'],
            ['des', 'JS']
        ]);
console.log(map.entries())	// MapIterator {"name" => "An", "des" => "JS"}
console.log(map.keys()) // MapIterator {"name", "des"}
console.log(map.values()
复制代码

const reporter = {
  report: function(key, value) {
    console.log("Key: %s, Value: %s", key, value);
  }
};

let map = new Map([
    ['name', 'An'],
    ['des', 'JS']
])
map.forEach(function(value, key, map) {
  this.report(key, value);
}, reporter);
// Key: name, Value: An
// Key: des, Value: JS

forEach的第二个参数为reporter对象,那么this就只想这个对象。
复制代码

Map转为其余类型

Map 转 Array

const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log([...map])	// [[1, 1], [2, 2], [3, 3]]
复制代码

Array 转 Map

const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log(map)	// Map {1 => 1, 2 => 2, 3 => 3}

复制代码

Map 转 Object

由于 Object 的键名都为字符串,而Map 的键名为对象,因此转换的时候会把非字符串键名转换为字符串键名。

function mapToObj(map) {
    let obj = Object.create(null)
    for (let [key, value] of map) {
        obj[key] = value
    }
    return obj
}
const map = new Map().set('name', 'An').set('des', 'JS')
mapToObj(map)  // {name: "An", des: "JS"}

复制代码

Object 转 Map

function objToMap(obj) {
    let map = new Map()
    for (let key of Object.keys(obj)) {
        map.set(key, obj[key])
    }
    return map
}

objToMap({'name': 'An', 'des': 'JS'}) // Map {"name" => "An", "des" => "JS"}

复制代码

Map 转 JSON

function mapToJson(map) {
    return JSON.stringify([...map])
}

let map = new Map().set('name', 'An').set('des', 'JS')
mapToJson(map)	// [["name","An"],["des","JS"]]

复制代码

JSON 转 Map

function jsonToStrMap(jsonStr) {
  return objToMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"name": "An", "des": "JS"}') // Map {"name" => "An", "des" => "JS"}

复制代码

WeakMap

注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。

WeakMap 中,每一个键对本身所引用对象的引用都是弱引用,在没有其余引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),因此,WeakMap 的 key 是不可枚举的。

属性:

  • constructor:构造函数 方法:

  • has(key):判断是否有 key 关联对象

  • get(key):返回key关联对象(没有则则返回 undefined)

  • set(key):设置一组key关联对象

  • delete(key):移除 key 的关联对象

let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();

myWeakmap.set(myElement, {timesClicked: 0});

myElement.addEventListener('click', function() {
  let logoData = myWeakmap.get(myElement);
  logoData.timesClicked++;
}, false);

复制代码

总结:

- Set 集合

成员惟1、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
能够遍历,方法有:add、delete、has

- WeakSet

成员都是对象
成员都是弱引用,能够被垃圾回收机制回收,能够用来保存DOM节点,不容易形成内存泄漏
不能遍历,方法有add、delete、has

- Map 字典

本质上是键值对的集合,相似集合
能够遍历,方法不少能够跟各类数据格式转换

- WeakMap

只接受对象做为键名(null除外),不接受其余类型的值做为键名
键名是弱引用,键值能够是任意的,键名所指向的对象能够被垃圾回收,此时键名是无效的
不能遍历,方法有get、set、has、delete
复制代码

4、问题四: 请描述下状态码304?

请描述下状态码304?
复制代码

回答区:

304 表示客户端有缓冲,而且服务端的资源未更新,能够直接使用客户端的,不须要再向服务器获取
复制代码

经常使用状态码:

5、问题五: 写出5种css隐藏元素的办法

写出5种css隐藏元素的办法
复制代码

回答区

opacity: 0;

visibility: hidden;

display: none;

position: absolute; top: -9999px; left: -9999px;

clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px); // 剪切掉
复制代码

clip-path 案例1

clip-path 案例2

6、问题六: cookies 与session 有什么区别?

cookies 与session 有什么区别?
复制代码

回答区

Cookies 机制

Http是无状态的协议,所以没法从网络链接上来肯定用户的身份,因此就诞生的Cookies。

客户端第一次访问服务器的时候,服务器就会生成一个cookie,并颁发给客户端,这就是你的凭据了。当客户端下次访问的时候就会携带这个Cookies,服务器检查该Cookie,以此来辨认用户状态。服务器还能够根据须要修改Cookie的内容。

cookie的内容主要包括name(名字)value(值)maxAge(失效时间)path(路径),domain(域)secure.

Cookies 怎么使用在server端的呢?

一、用户初次访问,服务器在response的header中设置`Set-Cookie`,具体值由server端具体决定,这些内容包含一些铭感信息,并不安全。
二、客户端收到响应后,就会在客户端设置cookies
三、下一次访问接口,就会在request带上cookie,
四、server端拿到cookie,查看cookies信息,服务端经过返回的cookie信息去判断本次链接的状态,用户的状态。
五、Cookies能够用在用户登录后状态的保持
复制代码

Session 机制

Session 是一种服务端机制,存储在服务器中,用一种相似散列表的结构来保存信息。

服务器首先检查这个客户端里的请求里是否已包含了一个session标识--sessionID,若是已经包含一个sessionID,则说明之前已经为此客户端建立过session,服务器就按照sessionID把这个session检索出来使用,若是客户端请求不包含sessionID,则为此客户端建立一个session而且声称一个与此session相关联的sessionID,

所以在客户端只存一个sessionId,每次请求带上这个。

session 怎么使用在server端的呢?

客户端第一次访问,服务端判断传入的cookies中是否含有sessionId,若是有就去sessionData中去查询是否记录过这条id,而后找到对应信息,若是没有就建立一个sessionId,插入到sessionData中。并在response中设置cookie,将sessionId设置进去,存到客户端。客户端下次的时候就会带上了。这里的sessionData保存在server进程内存中,这种方式并很差,咱们会用redis去解决。
复制代码

- cookies
因为http请求是无状态的,须要cookie来作身份验证
1.cookies由服务端建立,发送给浏览器端,存与浏览器端,当用户发出请求后,带上cookie发送给服务端作验证。
2.来回传递过程当中,占用带宽,消耗网络资源,而且容易被中间人获取或浏览器端用户篡改十分不安全
3.cookies大小只有4k
4.方便js来操做数据


- session
1.session主要存在于服务端,不会被发送到浏览器因此很安全
2.若是没有设置过时时间,在会话结束后session会自动消失
3.session主要用于服务端保存请求信息的机制
4.服务端会为每个用户设置一个ID,高效安全。
复制代码

7、问题七: 数组去重

数组去重
复制代码

回答区

咱们可使用不一样的方法来实现

// 测试数据 10万数据

const arr = [];
// 生成[0, 100000]之间的随机数
for (let i = 0; i < 100000; i++) {
  arr.push(0 + Math.floor((100000 - 0 + 1) * Math.random()))
}
复制代码

【1】双重循环法

// 数组去重
  function queue(arr) {
    var newArray = [];
    var isRepeat = false;

    for(var i = 0; i < arr.length; i++) {

      // 检查是否重复
      isRepeat = false;
      for(var j = 0; j < newArray.length; j++) {
        if(newArray[j] === arr[i]) {
          isRepeat = true;
          break;
        }
      }

      if(!isRepeat) {
        newArray.push(arr[i]);
      }
    }
    return newArray;
  }

  var array = [1,2,3,4,5,6,2,3,4];
  var array1 = ['1xxx','3','2','3','2', 100];

  console.log(queue(array));
  console.log(queue(array1));
复制代码
time: 3688.440185546875ms
复制代码

【2】Array.prototype.indexOf()

// 数组去重
  Array.prototype.queue = function () {
    var newArray = [];
    this.forEach((item, index) => {
      if(newArray.indexOf(item) == -1) {
        newArray.push(item);
      }
    })
    return newArray;
  }
  var array = [1,2,3,4,5,6,2,3,4];
  var array1 = ['1xxx','3','2','3','2', 100];

  console.log(array.queue());
  console.log(array1.queue());
复制代码
time: test2: 3766.324951171875ms
复制代码

【3】Array.prototype.sort()

// 数组去重
    Array.prototype.queue = function () {
      var newArray = [];
      this.sort();
      for (var i = 0; i < this.length; i++) {
        if (this[i] !== newArray[newArray.length - 1]) {
          newArray.push(this[i]);
        }
      }
      return newArray;
    }
    var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
    var array1 = ['1xxx', '3', '2', '3', '2', 100];

    console.log(array.queue());
    console.log(array1.queue());
复制代码
time: 121.6259765625ms
复制代码

【4】Map

// 数组去重
    Array.prototype.queue = function () {
      const map = new Map();
      return this.filter((item, index) => {
        return !map.has(item) && map.set(item, 1)
      })
    }
    var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
    var array1 = ['1xxx', '3', '2', '3', '2', 100];
复制代码

或者

// 数组去重
    Array.prototype.queue = function () {
      var newArray = [];
      const map = new Map();
      for (var i = 0; i< this.length; i++) {
        if( !map.get(this[i])) {
          map.set(this[i], 1);
          newArray.push(this[i])
        }
      }
      return newArray;
    }
    var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
    var array1 = ['1xxx', '3', '2', '3', '2', 100];

    console.log(array.queue());
    console.log(array1.queue());
复制代码
test1: 27.89697265625ms
test2: 21.945068359375ms
复制代码

【5】Set

// 数组去重
Array.prototype.queue = function () {
  const set = new Set(this);
  return Array.from(set);
}
var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
var array1 = ['1xxx', '3', '2', '3', '2', 100];

console.log(array.queue());
console.log(array1.queue());
复制代码
// 数组去重
Array.prototype.queue = function () {
  return [...new Set(this)];
}
var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
var array1 = ['1xxx', '3', '2', '3', '2', 100];

console.log(array.queue());
console.log(array1.queue());
复制代码
test1: 36.8046875ms
test2: 31.98681640625ms
复制代码

除了考虑时间复杂度外、性能以外,还要考虑数组元素的数据类型

通过综合考虑,最优的数组去重算法是采用Map数据结构实现的算法。

8、问题八: 将this is a pen首字母大写

将this is a pen首字母大写
复制代码

回答区

var str = "this is a pen";

function ToUpper(str) {
  var arr = str.split(" ").map((item, index) => {
    return item.slice(0,1).toUpperCase() + item.slice(1)
  })

  return arr.join(" ");
}

console.log(ToUpper(str)); // This Is A Pen
复制代码

或者

var str = "this is a pen";

function ToUpper(str) {
  // 正则匹配
  var s =  str.toLowerCase().replace(/\b\w+\b/g, function (item) {
    return item.slice(0,1).toUpperCase() + item.slice(1)
  })

  return s;
}

console.log(ToUpper(str)); // This Is A Pen
复制代码

9、问题九: 关于this的题目

关于this的题目
复制代码

案例1

var x = 10;
var obj = {
    x: 20,
    f: function(){
        console.log(this.x);        // 20
        var foo = function(){ 
            console.log(this.x);    
            }
        foo();                      // 10
    }
};
obj.f();
复制代码

解析:

var x = 10;
var obj = {
    x: 20,
    f: function(){
        console.log(this.x);        // 20 这里是隐性绑定
        var foo = function(){ 
            console.log(this.x);    
            }
        foo();                      // 10 这里一看是光杆司令,不要误认为foo 在函数 f 里,也在 f 里执行,this必然就跟f里面的this同样。
    }
};
obj.f();

复制代码

案例2

function foo(arg){
    this.a = arg; // window.a = arg
    return this  // return window
};

var a = foo(1);  // window.a = window
var b = foo(10);

console.log(a.a);    // undefined // window.a.a
console.log(b.a);    // 10 // window.a
复制代码

案例3

var x = 10;
var obj = {
    x: 20,
    f: function(){ console.log(this.x); }
};
var bar = obj.f;
var obj2 = {
    x: 30,
    f: obj.f
}
obj.f(); // 20
bar();  // 10 
obj2.f(); // 30
复制代码

10、问题十: 你能描述一下渐进加强和优雅降级吗?

你能描述一下渐进加强和优雅降级吗?
复制代码

在咱们开发项目的时候,咱们使用的css3的新属性挺多的。最初css3的时候不少浏览器不支持,当时css3标准仍是刚出来并无好的标准,各浏览器厂商按照草案,制定了本身的实现,只是须要在属性前面添加不一样浏览器的前缀。当标准确立后,各大浏览器开始逐步支持不带前缀的css3新属性。

下面有两种兼容写法:

.transition {
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
.transition {
transition: all .5s;
-o-transition: all .5s;
-moz-transition: all .5s;
-webkit-transition: all .5s;
}
复制代码

这就引出了两个概念:优雅降级和渐进加强。

渐近加强

渐近加强认为项目应该注重自己,首先应该针对低版本的浏览器实现最基本的功能,以后在针对高级浏览器进行局部调整。换句话说,就是以最低要求,实现最基础功能为基本,向上兼容。 如下写法就是渐近加强。

.transition {
    -webkit-transition: all .5s;
    -moz-transition: all .5s;
    -o-transition: all .5s;
    transition: all .5s; // 高浏览器支持
}
复制代码

优雅降级

优雅降级则是首先在高级别浏览器开发,构建整个页面,完善功能。而后针对部分低版本浏览器在作兼容处理。总之即是,高版本为基准,向下兼容。

如下写法就是渐近加强。

.transition {
    transition: all .5s;
    -o-transition: all .5s;
    -moz-transition: all .5s;
    -webkit-transition: all .5s;
}
复制代码

渐进加强和优雅降级的区别

  • 一、兼容不一样 一个是向上兼容,一个是向下兼容。

  • 二、成本上分析

    若是你采用渐进加强的开发流程,先作一个基本功能版,而后针对各个浏览器进行渐进增长,增长各类功能。相对于优雅降级来讲,开发周期长,初期投入资金大。

  • 三、技术上分析

    1. 好久好久之前:浏览器即不宠幸前缀CSS3也不宠幸纯情CSS3(border-radius);
    2. 不久以前:浏览器只宠幸前缀CSS3,不宠幸纯情的CSS3;
    3. 如今:浏览器不只宠幸前缀CSS3属性,还宠幸纯情CSS3属性;
    4. 等到之后:前缀CSS3就回乡下带孩子了,浏览器只宠幸纯情CSS3属性。

看上图咱们可以区分带前缀属性和不带前缀属性在浏览器的最终处理结果。咱们如今正处于 浏览器不只宠幸前缀CSS3属性,还宠幸纯情CSS3属性;的阶段。

当下,webkit核心的浏览器不只支持border-radius属性,也支持-webkit-border-radius属性,这自己没什么,只是效果会出现不一样。

.content{
    width: 200px;
    height: 80px;
    background: #444;
    border-radius: 30px 10px;
    -webkit-border-radius: 30px 10px;
    margin-bottom: 40px;
  }

  .content1{
    width: 200px;
    height: 80px;
    background: #444;
    -webkit-border-radius: 30px 10px;
    border-radius: 30px 10px;
  }

  <div class="content"></div>
  <div class="content1"></div>

复制代码

按理说这两种写法效果应该是同样的,可是咱们如今浏览器停留在操蛋的第三阶段,也就是如今,既支持前缀写法,又支持正常写法,这样就要出问题了。

最终效果上图。咱们想要实现的效果是下面的,但是上面是什么鬼?

当属性超过一个参数值的时候,不一样属性产生的做用是不同的!

能够看到,采用优雅降级的写法,若是一个浏览器同时支持前缀写法和正常写法,后面的旧版浏览器样式就覆盖了新版样式,出现一些奇怪的问题 ,可是用渐进加强的写法就不存在这个问题。

建议: 在书写css的时候采用渐近加强的写法。 咱们网页项目的开发最好采用优雅降级的方式开发,这样效率更高。固然也不是绝对的,要看你用户使用的浏览器的占比了。

11、问题十一: CSS 中可让文字垂直和水平方向上重叠的两个属性是什么?

CSS 中可让文字垂直和水平方向上重叠的两个属性是什么?
复制代码

回答区:

垂直方向: line-height
水平方向: letter-spacing
复制代码
<style type="text/css"> h1 {letter-spacing: -4px} h4 {line-height: 0px} </style>

<body>
<h1>This is header 1</h1>
<h4>This is header 4</h4>
</body>

复制代码

效果以下图:

<style type="text/css"> p.small {line-height: 100%} p.small1 {line-height: 90%} </style>


<body>
<h1>正常高度</h1>
<p>
这是拥有标准行高的段落。
在大多数浏览器中默认行高大约是 110% 到 120%。
这是拥有标准行高的段落。
这是拥有标准行高的段落。
这是拥有标准行高的段落。
这是拥有标准行高的段落。
这是拥有标准行高的段落。
</p>
<h1>100%</h1>
<p class="small">
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
</p>

<h1>90%</h1>
<p class="small1">
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
这个段落拥有更小的行高。
</p>
</body>
复制代码

效果以下图:

12、问题十二: 如何解决使用inline-block引发的空白间隙的问题

如何解决使用inline-block引发的空白间隙的问题
复制代码

回答区:

  • 方法一: 既然空白间隙是因为换行或空格引发,那么消除换行符不就解决了

  • 方法二: 设置父元素的font-size为0,在子元素从新设置字体大小

    ie8,firefox,chrome 和 opera 浏览器下已经没有问题了,可是在 低版本safari 浏览器下仍是有问题。

  • 方法三: 利用负margin-left(不推荐,具体负margin多少取决于字体的大小)

  • 方法四: letter-spacing(字符边距):负值 or word-spacing(单词边距) 负值,负值大小仍是取决于字体

  • 方法五: 【推荐】给父元素 设置font-size:0 ;letter-spacing:-3px ,子元素从新设置font-size。

十3、问题十三: 使用css建立一个三角形

使用css建立一个三角形
复制代码

回答区:

如下是原理图:

.kailong{
      width: 0px;
      height: 0px;
      border-right: 50px solid transparent;
      border-left: 50px solid transparent;
      border-bottom: 50px solid red;
}
复制代码

  • 实心三角
#demo {
      width: 100px;
      height: 100px;
      background-color: #333;
      position: relative;
    }

    #demo:after {
      border: solid transparent;
      border-left-color: #333;
      border-width: 10px;
      width: 0;
      content: " ";
      position: absolute;
      left: 100%;
      top: 10%;
    }
复制代码

  • 空心三角
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style> #demo { width: 100px; height: 100px; border: 1px solid #333; position: relative; } /*小三角*/ #demo:after { border: solid transparent; border-left-color: #fff; border-width: 10px; content: " "; position: absolute; left: 100%; top: 20px; /*20px*/ } /*大三角*/ #demo:before { border: solid transparent; border-left-color: #000; border-width: 12px; /*10px+2px*/ content: " "; position: absolute; left: 100%; top: 18px; /*20px-2px*/ } /* 小三角遮不住大三角 */ </style>
</head>

<body>
  <div id="demo"></div>
</body>

</html>
复制代码

十4、问题十四: 有一个长度为 100 的数组,请求出该数组的前 10 个元素之和。

有一个长度为 100 的数组,请求出该数组的前 10 个元素之和。
复制代码

回答区:

var arr = [1,2,3,4,5,6,7,8,9,10,23,34,45,56,45,34];

arr.slice(0, 10).reduce((a,b) => {
    return a+b
})

// 55
复制代码

十5、问题十五: 写一个程序打印 1 到 100 这些数字,遇到数字为 3 的倍数,打印 “A” 替代该数字;遇到 5 的倍数,用 “B” 代替;遇到便是 3 的倍数又是 5 的倍数,打印 “AB”。

写一个程序打印 1 到 100 这些数字,遇到数字为 3 的倍数,打印 “A” 替代该数字;遇到 5 的倍数,用 “B” 代替;遇到便是 3 的倍数又是 5 的倍数,打印 “AB”。
复制代码

回答区:

for(var i = 1 ; i <= 100; i++) {
    if(i%3 == 0 && i % 5 == 0) {
      console.log("AB")
    } else if (i % 3 == 0){
      console.log("A")
    } else if (i % 5 == 0) {
      console.log("B")
    } 
  }
复制代码

要点: 先排出要求最严格的

十6、问题十六: 引发内存泄漏的状况有?

引发内存泄漏的状况有?
复制代码
  • 垃圾回收机

JS哪些操做会形成内存泄露

回答区:

十7、问题十七: 写 React / Vue 项目时为何要在组件中写 key,其做用是什么

写 React / Vue 项目时为何要在组件中写 key,其做用是什么
复制代码

回答区

要理解key的用法咱们就不得不理解一下diff算法了,咱们知道,vue和react都实现了一套虚拟DOM,使咱们能够不直接操做DOM元素,只操做数据即可以从新渲染页面。而隐藏在背后的原理即是其高效的Diff算法。

diff算法只比较同层的节点,若是节点类型不一样,直接干掉前面的节点,再建立并插入新的节点,不会再比较这个节点之后的子节点了。若是节点类型相同,则会从新设置该节点的属性,从而实现节点的更新。

好比咱们有以下状况:

咱们但愿能够在B和C之间加一个F,Diff算法默认执行起来是这样的:

在没有key的状况下,会原地复用,修改节点信息,最后还会新增一个节点。

即把C更新成F,D更新成C,E更新成D,最后再插入E,这样效率较低。

diff算法就是利用key值去肯定咱们的节点,从而可以更快的找到正确的位置区插入新的节点。

diff算法用于比对新旧虚拟DOM对象,当咱们在比较头尾节点无果后,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点。若是没找到就认为是一个新增节点。若是找到了就去比对,而后更新节点。

总之: key是给每个vnode的惟一id,能够依靠key,更准确, 更快的拿到oldVnode中对应的vnode节点。从而是的diff算法可以更快更高效的更新咱们的虚拟dom。 参考回答

十8、问题十八: 什么是防抖和节流?有什么区别?

什么是防抖和节流?有什么区别?
复制代码

回答区

防抖和节流都是用来限制咱们事件触发的频率,从而提升浏览器的性能。

防抖: 触发事件n秒以后才会执行,可是若是在这个时间到来以前,咱们又触发了事件,则清除掉上一次的定时器,而后重新定时。最终实现的效果就是咱们连续触发事件时候,只会在最后触发咱们的事件函数。

节流: 在防抖的基础上进行改进,容许咱们在连续触发的过程当中,在固定时间段内能够触发事件函数,因此其实是在稀释函数的执行频率。

十9、问题十九: 介绍下深度优先遍历和广度优先遍历,如何实现?

介绍下深度优先遍历和广度优先遍历,如何实现?有什么区别?
复制代码

回答区

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
</head>

<body>
  <div id="parent">
    <div class="child-1">
      <div class="child-1-1">
        <div class="child-1-1-1">
          child-1-1-1
        </div>
      </div>
    </div>
    <div class="child-2">
      child2
    </div>
    <div class="child-3">
      <div class="child-3-1">
        child-3-1
      </div>
      <div class="child-3-2">
        child-3-2
      </div>
    </div>
  </div>

  <script>
    var a = 10;


    // 深度优先搜索
    let deepTraversal2 = (node) => {
      let nodes = []
      if (node !== null) {
        nodes.push(node)
        let children = node.children
        for (let i = 0; i < children.length; i++) {
          nodes = nodes.concat(deepTraversal2(children[i]))
        }
      }
      return nodes
    }


    // 测试深度
    // var dom = document.getElementById('parent');
    // console.log(deepTraversal2(dom));



    // 广度优先遍历
    function widthTraversal2 (node) {
      let nodes = [];
      let stack = [];

      if(node != null) {
        stack.push(node);

        while(stack.length) {
          let item = stack.shift();
          let children = item.children
          nodes.push(item)
          for(var i = 0; i < children.length; i++) {
            stack.push(children[i])
          }
        }
      }
      return nodes;
    }

    var dom = document.getElementById('parent');
    console.log(widthTraversal2(dom));
  </script>
</body>

</html>
复制代码

二10、问题二十: 你了解EventLoop吗?

你了解EventLoop吗?
复制代码

回答区

众所周知 JS 是门非阻塞单线程语言,由于在最初 JS 就是为了和浏览器交互而诞生的。若是 JS 是门多线程的语言话,咱们在多个线程中处理 DOM 就可能会发生问题(一个线程中新加节点,另外一个线程中删除节点),固然能够引入读写锁解决这个问题。

JS 在执行的过程当中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。若是遇到异步的代码,会被挂起并加入到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出须要执行的代码并放入执行栈中执行,因此本质上来讲 JS 中的异步仍是同步行为。

console.log('script start')

setTimeout(function() {
  console.log('setTimeout')
}, 0)

console.log('script end')
复制代码

以上代码虽然 setTimeout 延时为 0,其实仍是异步。这是由于 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,不足会自动增长。因此 setTimeout 仍是会在 script end 以后打印。

不一样的任务源会被分配到不一样的 Task 队列中,任务源能够分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobsmacrotask称为 task

微任务包括 process.nextTickpromise

宏任务包括scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

console.log('script start');

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')
// script start => Promise => script end => promise1 => promise2 => setTimeout
复制代码

首先执行同步代码,遇到promise的话,会首先执行内部的同步代码,而后再继续执行同步代码。途中遇到的settimeout和promise放入不一样的任务队列中,这时候因为执行栈已经为空,因此须要开始执行异步任务,首先查看微任务队列,发现又promise已经能够了,那么就执行promise的then,把全部能够执行的微任务都执行完成以后才会去宏任务队列找,发现又setTimeout能够执行了,就执行内部的代码。

因此正确的一次 Event loop 顺序是这样的

执行同步代码,这属于宏任务
执行栈为空,查询是否有微任务须要执行
执行全部微任务
必要的话渲染 UI
而后开始下一轮 Event loop,执行宏任务中的异步代码
复制代码

总结js异步执行机制

JS 主线程拥有一个 执行栈(同步任务) 和 一个 任务队列(microtasks queue),主线程会依次执行代码,

1 首先顺序执行同步代码

2 当遇到task任务(异步)时,会先执行必定的同步任务,而后让主线程继续执行下去,
而真正的task任务将交给浏览器内核 执行,浏览器内核执行结束后,会将该任务事先定义好的回调函数加入相应的任务队列中,
同时设置事件(当回调能够执行的时候通知)

3 当JS主线程清空执行栈以后,会按先入先出的顺序读取microtasks queue中的回调函数,并将该函数入栈,
继续运行执行栈,直到清空执行栈,再去读取任务队列。

4 当microtasks queue中的任务执行完成后,会提取 macrotask queue 的一个任务加入 microtask queue, 
接着继续执行microtask queue,依次执行下去直至全部任务执行结束。


这就是 JS的异步执行机制
复制代码

二11、问题二十一: async await、Promise、setTimeout

async / await、Promise、setTimeout
复制代码

回答区

async function async1(){
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2(){
  console.log('async2')
}
console.log('script start')
setTimeout(function(){
  console.log('setTimeout') 
},0)  
async1();
new Promise(function(resolve){
  console.log('promise1')
  resolve();
}).then(function(){
  console.log('promise2')
})
console.log('script end')

/* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */
复制代码

在解这个以前咱们有几个知识点要清楚:

一、js是单线程的。
二、promise被定义后是当即执行的,可是他的resolve是异步的。
三、promise的异步优先级高于setTimeout。由于promise是微任务
四、async会返回一个promise对象,await关键字会让出线程。
复制代码

接下在咱们分析下代码执行流程:

1、执行console.log('script start'),输出script start;
2、执行setTimeout,是一个异步动做,放入异步队列中;
3、执行async1(),输出async1 start,继续向下执行;
4、执行async2(),输出async2,并返回了一个promise对象,await让出了线程,
把返回的promise加入了异步队列,因此async1()下面的代码也要等待上面完成后继续执行;
5、执行 new Promise,输出promise1,而后将resolve放入异步队列;
6、执行console.log('script end'),输出script end;
7、到此同步的代码就都执行完成了,而后去异步队列里去获取任务,如今队列中有一个promise(async2返回的),resolve(new Promise的),和setTimeout,
由于promise属于微任务,先取出promise执行,默认返回resolve,再次加入了异步队列,如今就队列就变成了 `resolve(async2返回的promise返回的)``resolve(new Promise的)``setTimeout`8、接下来执行resolve(async2返回的promise返回的),输出了`async1 end`9、而后执行`resolve(new Promise的)`,输出了`promise2`10、最后执行`setTimeout`,输出了`settimeout`复制代码

题目的本质,就是考察setTimeout、promise、async await的实现及执行顺序,以及JS的事件循环的相关问题。

一篇讲解的很是好的事件循环的文章

二12、问题二十二: Async/Await 如何经过同步的方式实现异步

Async/Await 如何经过同步的方式实现异步
复制代码

回答区

深刻理解 async / await

二十3、问题二十三: 算法手写题

已知以下数组:
var arr = [ 
    [1, 2, 2], 
    [3, 4, 5, 5], 
    [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 
    10
];
编写一个程序将数组扁平化去并除其中重复部分数据,最终获得一个升序且不重复的数组
复制代码

回答区

最后就剩排序了。

二十4、问题二十四: JS异步解决方案的发展历程以及优缺点。

JS异步解决方案的发展历程以及优缺点。
复制代码

回答区

1. 回调函数(callback)

setTimeout(() => {
    // callback 函数体
}, 1000)
复制代码

缺点:回调地狱,不能用 try catch 捕获错误,不能 return

回调地狱的根本问题在于:

缺少顺序性: 回调地狱致使的调试困难,和大脑的思惟方式不符
嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转)
嵌套函数过多的多话,很难处理错误
复制代码
ajax('XXX1', () => {
    // callback 函数体
    ajax('XXX2', () => {
        // callback 函数体
        ajax('XXX3', () => {
            // callback 函数体
        })
    })
})
复制代码
  • 优势:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)

2. Promise

Promise就是为了解决callback的问题而产生的。

Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,若是咱们在 then 中 return ,return 的结果会被 Promise.resolve() 包装

优势:解决了回调地狱的问题

ajax('XXX1')
  .then(res => {
      // 操做逻辑
      return ajax('XXX2')
  }).then(res => {
      // 操做逻辑
      return ajax('XXX3')
  }).then(res => {
      // 操做逻辑
  })
复制代码

缺点:没法取消 Promise ,错误须要经过回调函数来捕获

3. Async/await

async、await 是异步的终极解决方案

优势是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题

缺点:await 将异步代码改形成同步代码,若是多个异步操做没有依赖性而使用 await 会致使性能上的下降。

async function test() {
  // 如下代码没有依赖性的话,彻底可使用 Promise.all 的方式
  // 若是有依赖性的话,其实就是解决回调地狱的例子了
  await fetch('XXX1')
  await fetch('XXX2')
  await fetch('XXX3')
}
复制代码

async 函数返回的 Promise 对象,必须等到内部全部的 await 命令的 Promise 对象执行完,才会发生状态改变

二十5、问题二十五: 面试可能会问到的一些网络请求问题

面试可能会问到的一些网络请求问题
复制代码

一、关于网络请求的疑问

  • Ajax的出现解决了什么问题
  • 原生Ajax如何使用
  • jQuery的网络请求方式

二、Ajax的出现解决了什么问题

在Ajax出现以前,web程序是这样工做的:

这种交互的的缺陷是显而易见的,任何和服务器的交互都须要刷新页面,用户体验很是差,Ajax的出现解决了这个问题。

使用Ajax,网页应用可以快速地将增量更新呈如今用户界面上,而不须要重载(刷新)整个页面。

ajax能够实现,咱们发送请求,获取相应的数据,而后经过js去动态渲染页面,而不须要服务器拼接HTML,页面的刷新也只是局部的刷新,再也不是整个页面的刷新了。

三、原生Ajax的用法

手写一个原生的ajax

var xhr = new XMLHttpRequest();
    xhr.open('post', 'http://', true);
    xhr.onreadystatechange = function() {
      if(xhr.readyState == 4) {
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
          console.log(xhr.responseText);
        }
       }
    }

    let postData = {"name1":"value1","name2":"value2"};
    postData = (function(value) {
      var dataString = "";
      for(var key in value){
           dataString += key+"="+value[key]+"&";
      };
      return dataString;
    })(postData);

    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    // 异常处理
    xhr.onerror = function() {
       console.log('Network request failed')
    }
    // 跨域携带cookie
    xhr.withCredentials = true;
    xhr.send(postData);
复制代码

  • readyState

用来标识当前XMLHttpRequest对象所处的状态,XMLHttpRequest对象老是位于下列状态中的一个:

  • status

表示http请求的状态, 初始值为0。若是服务器没有显式地指定状态码, 那么status将被设置为默认值, 即200。

  • responseType

表示响应的数据类型,并容许咱们手动设置,若是为空,默认为text类型,能够有下面的取值:

  • onreadystatechange

当readyState属性发生变化时,callback会被触发。

  • onprogress
xhr.onprogress = function(event){
  console.log(event.loaded / event.total);
}
复制代码

回调函数能够获取资源总大小total,已经加载的资源大小loaded,用这两个值能够计算加载进度。

  • onerror

当ajax资源加载失败时会触发callback。

四、jQuery对Ajax的封装

在很长一段时间里,人们使用jQuery提供的ajax封装进行网络请求,包括.ajax、.get、$.post等,这几个方法放到如今,我依然以为很实用。

$.ajax({
    dataType: 'json', // 设置返回值类型
    contentType: 'application/json', // 设置参数类型
    headers: {'Content-Type','application/json'},// 设置请求头
    xhrFields: { withCredentials: true }, // 跨域携带cookie
    data: JSON.stringify({a: [{b:1, a:1}]}), // 传递参数
    error:function(xhr,status){  // 错误处理
       console.log(xhr,status);
    },
    success: function (data,status) {  // 获取结果
       console.log(data,status);
    }
})
复制代码

二十6、问题二十六: 介绍模块化发展历程

跨域方案
复制代码

模块化主要是用来抽离公共代码,隔离做用域,避免变量冲突等。

【1】IIFE 使用自执行函数来编写模块化

(function(){
  return {
	data:[]
  }
})()
复制代码

特色: 在一个单独的函数做用域中执行代码,避免变量冲突。

【2】AMD 使用requireJS 来编写模块化

define('./index.js',function(code){
	// code 就是index.js 返回的内容
})
复制代码

特色: 依赖必须提早声明好。

【3】CMD 使用seaJS 来编写模块化

define(function(require, exports, module) {  
  var indexCode = require('./index.js');
});
复制代码

特色: 支持动态引入依赖文件。

【4】CommonJS nodejs 中自带的模块化

var fs = require('fs');
复制代码

【5】ES Modules ES6 引入的模块化,支持import 来引入另外一个 js 。

import a from 'a';
复制代码

二十7、问题二十七: H5新增了哪些全局属性

全局属性
复制代码
html5新增:
一、contenteditable		元素内容可编辑
二、contextmenu			元素上下文菜单,目前只有火狐浏览器支持
三、data-*				用于存储页面私有数据
四、draggable			是否可拖动
五、spellcheck			属性规定是否对元素进行拼写和语法检查。
复制代码

二十8、问题二十八: 你知道Shadow DOM(影子DOM)吗?

你知道Shadow DOM(影子DOM)吗?
复制代码
Shadow DOM 是浏览器的一种行为,可以自动添加子元素,好比aduio元素,当咱们设置了controls的时候,在网页中会提供进度条、音量控制等。
复制代码

二十9、问题二十九: 你知道href和src的区别吗?

你知道href和src的区别吗?
复制代码
咱们能够这么理解:
href意思是打通当前文档和定义的资源的链接,即引用
src意思是将目标资源嵌入到页面中,即引入
复制代码

三10、问题三十: CSS样式的加载

CSS样式的加载方式:
复制代码

css应用到页面中的三种引用方式:内联、内嵌、外部样式。

方式 特殊性 http请求 重用范围 文档大小 伪类与伪元素
内联 最高 不可 增长 不可定义
内嵌 与外部相同 当前文档 增长 能够定义
外部 与内嵌相同 整个项目 保持 能够定义

三11、问题三十一: JS脚本加载的几种方式

JS脚本加载的几种方式
复制代码

三种方式:内嵌、外链、元素属性

  • 内嵌

若是将内敛脚本放置在样式表以后,那么会致使延迟下载。由于js脚本中可能存在对样式的修改,因此会等到样式表加载完毕以后,再继续执行脚本,而后才会继续解析后面的。因此这种方式并很差。因此通常都会吧script放在末尾附近。

  • 外链

三种状况:

<script>			
当解析文档过程当中遇到了script标签,会中断解析,下载js脚本并执行,
执行完毕以后在继续文档的解析
复制代码
<script defer>		
当解析文档过程当中遇到了script标签,并不会中断文档的解析,而是js脚本和文档解析一同进行,
等到文档解析完毕以后,再去执行刚才加载好的脚本。
复制代码
<script async>		
当解析文档过程当中遇到了script标签,并不会中断文档的解析,
而是js脚本和文档解析一同进行,当脚本加载完毕以后,就立马执行,
这时候文档的解析和脚本的执行是同时进行的。js脚本执行完毕以后,
文档解析继续进行。
复制代码

使用<script defer>跟把咱们的script标签放在body底部效果相似。

  • 元素属性
一、事件属性
<input type="button" onclick="print()" />
这种作法存在必定的局限性,不能写较为复炸的函数申明和建立对象,
代码量大的时候,可读性就会大大下降。

二、特殊协议 “javascript:”伪协议只能做用于某几个特定的属性。
<a href="javascript:void(0);"> 能够阻止a标签的默认行为(重定向)
void是运算符,会忽略计算结果返回undefined

“javascript:”伪协议的另外一个用途就是制做浏览器的书签
复制代码

三12、问题三十二: 你了解Meta标签吗?

你了解Meta标签吗?
复制代码

meta标签是空标签,有四个属性: name 、content、 charset、 http-equiv

  • charset 指定字符集
<meta charset="UTF_8" />
复制代码
  • name + content 定义页面的一些基本元数据信息,方便计算机的扫描阅读
<meta name="application-name" content="web应用名称" />
<meta name="author" content="web应用做者" />
<meta name="description" content="文档描述" />
<meta name="generator" content="生成页面的工具名" />
<meta name="keywords" content="关键字,每一个关键字能够用逗号分隔" />
<meta name="robots" content="规定搜索引擎如何操做本文档,如: noindex(禁止索引文档),noarchive(禁止缓冲文档内容)等" />
<meta name="viewport" content="定义视口" />
复制代码

咱们在作移动端开发的时候,须要作viewport适配。

<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; minimum-scale=1;user-scalable=no;">
复制代码
  • http-equiv
一、指定默认样式表
<meta http-equiv="default-style" content="red" />
上面 content 属性的值必须匹配同一文档中的一个 link 元素上的 title 属性的值,或者必须匹配同一文档中的一个 style 元素上的 title 属性的值。

二、refresh
定义文档自动刷新的时间间隔。
实例:
<meta http-equiv="refresh" content="300" />
<meta http-equiv="Refresh" content="2;URL=http://www.net.cn/" /> 
注释:值 "refresh" 应该慎重使用,由于它会使得页面不受用户控制。在 W3C's Web 内容可访问性指南 中使用 "refresh" 会到致使失败。 复制代码

总之,meta标签能够作如下事情:

一、声明文档的字符编码
二、定义网页的一些文档描述信息,方便计算机的阅读(搜索引擎)
三、用于适配移动端
四、调整文档的首选样式表、定时刷新等
复制代码

三十3、问题三十三: a标签的target属性你了解吗?

你了解Meta标签吗?
复制代码
_self		当前窗口
_blank		新窗口
_parent		父窗口,若是没有则是与_self效果相同
_top		顶层窗口,若是没有则是与_self效果相同
复制代码

经常使用的是_self和_target。

a标签还能够

<a href="tel:10086">拨打电话</a>
<a href="sms:10086?body=test">发送短信</a>
<a href="mailto:1392372716@qq.com?cc=jain@pp.com">发送邮件</a>
复制代码

三十4、问题三十四: disabled和readonly的区别?

disabled和readonly的区别?
复制代码
他们的写法同样:
disabled写法:<input type="text" name="aaa" value="xxx" disabled="true"/>

readonly写法:<input type="text" name="bbb" value="xxx" readonly="true"/>

相同点: 都能把咱们的文本表单设置为只读,不可编辑状态。
复制代码

不一样点:

一、样子效果不一样: disabled能把咱们的表单变成不可编辑状态的同时还能使得咱们的文本框变灰,
可是readonly不会。

二、readonly修饰下的表单元素,还能够经过js去获取文本内容,
可是disabled修饰下的文本不能经过js获取文本内容。

三、readonly没有disabled适用范围逛,disabled还能够适用于checkbox、
select等可是readonly没效果。
复制代码

三十5、问题三十五: 为何iframe用的愈来愈少了?

为何iframe用的愈来愈少了?
复制代码

经过Iframe可以在一个文档里面嵌入另外一个文档。而且这两个文档能够作到互不影响,JS和CSS都不会影响。Iframe存在着一些不足,可是在一些领域他在以前仍是至关有用的。最近的HTML5已正在逐步取代Iframe。

iframe常被用于复用部分界面,比较早期的网站使用 iframe,主要是用于导航栏(navigator)。为何?

由于一个网站不少页面的导航栏部分是相同的,在避免切换页面的时候重复下载,将导航栏和正文分开在 iframe 中,是一个方便的作法。同时带来的不利是,默认状况下,使用了 iframe 的网站的 URL 不会随着页面的变化而变化。这就意味着一旦刷新,网站可能又回到首页。

那么如今何时会用到 iframe 呢?

由于 iframe 的页面和父页面(parent)是分开的,因此它意味着,这是一个独立的区域,不受 parent 的 CSS 或者全局的 JavaScript 的影响。典型的,好比所见即所得的网页编辑器(WYSIWYG Online HTML Editor),由于它们须要 reset 本身的 CSS 到本身的标准,而不被 parent CSS 的 影响。

使用 iframe 是否是一个好的用法(good practice),不能一律而论,可是能够确定是,如今的大部分网站避免采用这种方式的。

由于它的这两个缺点,因此致使了它不能很好的使用下去:

(1)浏览器对同一域名下的并发请求作了限制,好比谷歌浏览器最大并发请求最大数为6,iframe中的文档与父文档共享连接,也就是说,父文档若是并发请求了5个资源,那么子文档中此时只能并发执行1个了,剩下的只能在下一次请求。iframe和主页面共享链接池,而浏览器对相同域的链接有限制,因此会影响页面的并行加载。 使用iframe以前须要考虑这两个缺点。若是须要使用iframe,最好是经过javascript 动态给iframe添加src属性值,这样能够绕开以上两个问题。

(2)子文档的加载会阻塞父文档的加载,load事件的触发时间差很少会增长一倍。

三十6、问题三十六: 为何再也不使用flash了?

为何再也不使用flash了?
复制代码

flash不但存在必定的安全隐患,并且没法很好的支持js和css,并且开发人员还需学习actionScript语法,数据处理并不方便,H5则是直接使用多媒体元素播放视频或者音频,不在依赖Flash。多媒体元素不只能够在浏览器中实现很好的实现,并且支持移动端设别,易于定制样式效果,可以使用Js和css操做咱们的多媒体元素,可以轻松实现响应式设计。

三十7、问题三十七: 图像

图像
复制代码
  • 位图图像

由像素矩阵组成,无数个像素点组成,位图图像的特色是色彩丰富,能够逼真的再现,所以经常使用于数码照相,页面效果等。canvas是基于位图的图像。

  • 矢量图像

由点和线组成,色彩比较简单,没法逼真呈现图像,可是当你放大的时候,不会丢失细节。SVG是基于矢量的图形。

三十8、问题三十八: 咱们前端须要从哪些方面注意SEO?

咱们前端须要从哪些方面注意SEO?
复制代码

SEO是(搜索引擎优化),搜索引擎会爬咱们的HTML页面,而后从中提取有用的信息进行统计整理,为了提升网页的排名度,不少大公司都强烈要求咱们的HTML要符合必定的规则书写。咱们大体须要注意如下几点:

一、经过meta标签来为咱们的网页添加一些元数据信息,好比关键字、描述、做者等,能够方便咱们爬中的信息采集
二、尽可能让每一个页面的title都有所不一样。
三、多使用语义化标签,不要滥用div等。尽可能让咱们的网页有提纲。
四、爬虫是不会去查看js文件的,因此不要把想给搜索引擎看的东西放入js中。
五、不要过多使用iframe,由于爬虫不会去找iframe。
六、图片尽可能写上alt。
复制代码
相关文章
相关标签/搜索