即将参加面试,逛了几个论坛学习了很多前端大牛的面试经,学习了很多,现收集总结与大家分享共同学习。
初学者阅后也要用心钻研其中的原理,重要知识需要系统学习、透彻学习,形成自己的知识链。临时抱佛脚只求面试侥幸混过关是错误的!也是不可能的!
本文比较基础精炼,欢迎评论指正,文章会不断更新。
? 文章列表
一、JavaScript
1.如何理解闭包
function A() {
let a = 1;
function B() {
console.log(a);
}
return B;
}
//函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包
//就是闭包
// 根据作用域链的规则,底层作用域没有声明的变量,会向上一级找,找到就返回,没找到就一直找,直到window的变量,没有就返回undefined。
var count = 10;
var cc = 'sss';
function add() {
var count = 0;
return function () {
console.log(count++);
console.log(this.count++);
console.log(cc);
}
}
var s = add();
s(); //0 10 sss
s(); //1 11 sss
s(); //2 12 sss
// 循环中的闭包
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
// 上面的代码不会输出数字 0 到 9,而是会输出数字 10 十次。
// 解决方法
// 为了正确的获得循环序号,最好使用 匿名包装器,其实就是我们通常说的自执行匿名函数。
for (var i = 0; i < 10; i++) {
(function (e) {
setTimeout(function () {
console.log(e);
}, 1000);
})(i);
}
// 从匿名包装器中返回一个函数
for (var i = 0; i < 10; i++) {
setTimeout((function (e) {
return function () {
console.log(e);
}
})(i), 1000)
}
// 使用let
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
2.JS继承的几种方式
<script>
var main = document.getElementById('main');
function log(s, color) {
this.color = color || '';
var p = document.createElement('p');
p.setAttribute('class', this.color);
p.innerHTML = s;
main.appendChild(p);
}
// JS继承实现方式
//定义一个动物类
function Animal(name) {
//属性
this.name = name || 'Animal';
//实例方法
this.sleep = function () {
return this.name + '正在睡觉!';
}
}
//原型方法
Animal.prototype.eat = function (food) {
return this.name + '正在吃' + food;
}
var ani = new Animal('cat');
//Test Code
log(ani.eat('fish'));
// 1.原型链继承
// 核心:将父类的实例作为子类的原型
// 特点
// 父类新增原型方法/原型属性,子类都能访问到
// 简单、易于实现
//
// 无法实现多继承
// 无法向父类构造函数传参
log('原型链继承', 'red')
function Cat() {
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
Cat.prototype.eateat = function (food) {
return 'cat又吃' + food;
}
//
//Test Code
var cat = new Cat();
log(cat.name);
log(cat.eat('fish'));
log(cat.sleep());
log(cat.eateat('fish'));
log(cat instanceof Animal); //true
log(cat instanceof Cat); //true
// 2.构造继承
// 核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类
// 特点:
// 可以实现多继承(call多个父类对象)
// 创建子类实例时,可以向父类传递参数
//
// 缺点只能继承父类的实例属性和方法,不能继承原型属性/方法
// 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
log('构造继承', 'red')
function Cat1(name) {
Animal.call(this);
this.name = name || 'Tom';
}
//Test Code
var cat1 = new Cat1();
log(cat1.name);
log(cat1.sleep());
log(cat1 instanceof Animal); //false
log(cat1 instanceof Cat1); //true
// 3.实例继承
// 核心:为父类实例添加新特性,作为子类实例返回
// 不支持多继承
log('实例继承', 'red');
function Cat2(name) {
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
//Test Code
var cat2 = new Cat2();
log(cat2.name);
log(cat2.sleep());
log(cat2 instanceof Animal); // true
log(cat2 instanceof Cat2); // false
// 4.拷贝继承
// 支持多继承
// 效率低,内存占用高(因为要拷贝父类属性)
log('拷贝继承', 'red');
function Cat3(name) {
var animal = new Animal();
for (var p in animal) {
Cat3.prototype[p] = animal[p];
}
Cat3.prototype.name = name || 'Tom';
}
// Test Code
var cat3 = new Cat3();
log(cat3.name);
log(cat3.sleep());
log(cat3 instanceof Animal); // false
log(cat3 instanceof Cat3); // true
// 5.组合继承
// 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
// 弥补了方式2的缺陷,可以继承实例属性或方法,也可以继承原型属性和方法
// 不存在引用属性共享问题
// 可传参
// 函数可复用
// 缺点 调用了两次父类构造函数,生成了两份实例
log('组合继承', 'red');
function Cat4(name) {
Animal.call(this);
this.name =name || 'Tom';
}
Cat4.prototype = new Animal();
//组合继承也需要修复构造函数指向
Cat4.prototype.constructor = Cat4;
var cat4 = new Cat4();
log(cat4.name);
log(cat4.sleep());
log(cat4 instanceof Animal); // true
log(cat4 instanceof Cat4); // true
// 6.寄生组合继承
// 核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法或属性,避免组合继承的缺点
// 堪称完美
// 实现较为复杂
log('寄生组合继承','red');
function Cat5(name) {
Animal.call(this);
this.name = name || 'Tom';
}
Cat5.prototype = Object.create(Animal.prototype);
// Object.create()的polyfill
/*
function pureObject(o){
//定义了一个临时构造函数
function F() {}
//将这个临时构造函数的原型指向了传入进来的对象。
F.prototype = obj;
//返回这个构造函数的一个实例。该实例拥有obj的所有属性和方法。
//因为该实例的原型是obj对象。
return new F();
}
*/
Cat5.prototype.constructor =Cat5;
// Test Code
var cat5 = new Cat5();
log(cat5.name);
log(cat5.sleep());
log(cat5 instanceof Animal); // true
log(cat5 instanceof Cat5); //true
//ES6 继承
class Animal {
constructor(name) {
this.name = name || 'Animal';
this.sleep = function () {
console.log('(Animal sleep) -> ' + this.name);
}
}
eat(food) {
console.log('(Animal eat) -> ' + food + ' -> ' + this.name);
}
}
class Cat extends Animal {
constructor(name) {
super(name || 'Cat');
}
eatFish(num) {
num = num || 'a little';
console.log('(Cat eatFish) -> ' + this.name + ' -> ' + num);
}
//重新声明父类同名方法会覆盖,ES5直接在原型链上操作
eat(food) {
console.log('(重写的)(Animal eat) -> ' + food + ' -> ' + this.name);
}
}
let animal = new Animal('Dog');
console.log(animal.name);
animal.sleep();
animal.eat('Meat');
console.log('-----------');
let cat = new Cat('Big Cat');
console.log(cat.name);
cat.sleep();
cat.eat('Small Fish');
cat.eatFish('more');
</script>
3.JS深浅拷贝
// 引入:
let a = {
age: 1
};
let b = a;
a.age = 2;
console.log(b.age); // 2
// 上述例子如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应改变。
// 浅拷贝
// 1.使用 Object.assign
// Object.assign是ES6新添加的接口,主要用来合并多个JavaScript对象。如果拷贝过来的属性的值是对象等复合属性
// 如果拷贝过来的属性的值是对象等复合属性,那么只能拷贝过来一个引用。
let aa = {
age: 1
};
let bb = Object.assign({}, aa);
aa.age = 2;
console.log(aa.age); // 2
console.log(bb.age); // 1
// 2.通过展开运算符(...)解决
let aaa = {
age: 1
};
let bbb = {...aaa};
aaa.age = 2;
console.log(aaa.age); // 2
console.log(bbb.age); // 1
// 深拷贝 // 所需拷贝的属性的值为对象等复合属性是使用
// 1.使用 JSON.parse(JSON.stringify(object)) (性能最优)
let x = {
age: 1,
jobs: {
first: 'first'
}
};
let y = JSON.parse(JSON.stringify(x));
x.jobs.first = 'second';
console.log(x.jobs.first); // second
console.log(y.jobs.first); // first
// 此方法局限性:
// 会忽略 undefined
// 不能序列化函数
// 不能解决循环引用的对象
4.JS拖拽
<script>
window.onload = function () {
var oDiv = document.getElementsByTagName("div")[0];
/*鼠标点击的位置距离DIV左边的距离 */
var disX = 0;
/*鼠标点击的位置距离DIV顶部的距离*/
var disY = 0;
oDiv.onmousedown = function () {
var e = e || window.event;
disX = e.clientX - oDiv.offsetLeft;
disY = e.clientY - oDiv.offsetTop;
document.onmousemove = function (e) {
var e = e || window.event;
// 横轴坐标
var leftX = e.clientX - disX;
// 纵轴坐标
var topY = e.clientY - disY;
if (leftX < 0) {
leftX = 0;
}
/* 获取浏览器视口大小 document.document.documentElement.clientWidth*/
else if (leftX > document.documentElement.clientWidth - oDiv.offsetWidth) {
leftX = document.document.documentElement.clientWidth - oDiv.offsetWidth;
}
if (topY < 0) {
topY = 0;
}
else if (topY > document.documentElement.clientHeight - oDiv.offsetHeight) {
topY = document.documentElement.clientHeight - oDiv.offsetHeight;
}
oDiv.style.left = leftX + "px";
oDiv.style.top = topY + "px";
}
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
}
}
}
</script>
5.Cookie使用
window.onload = function () {
document.cookie = "userID=10000";
document.cookie = "name1=10000";
//document.cookie = "name2=10000";
document.cookie = "username=John Smith; expires=Thu, 18 Dec 2019 12:00:00 GMT; path=/";
// expires:过期时间
//移除Cookie name2
//document.cookie = "name2=1;expires=-1";
alert(document.cookie);
//exdays: 过期天数
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function removeCookie(key) {
setCookie(key, "", -1);
}
removeCookie("name2");
setCookie('userID', '123', 600);
alert(getCookie("userID"));
}
6.Promise简化ajax异步处理
function myXHR(method, url, data) {
var requset = new XMLHttpRequest();
return new Promise((resolve, reject) => {
requset.onreadystatechange = function () {
if (requset.readyState === 4) {
if (requset.status === 200) {
resolve(requset.responseText);
}
else {
reject(requset.status);
}
}
}
requset.open(method, url);
requset.send(data);
});
}
var p = myXHR('GET', 'url:');
p.then(responseText => {
console.log(responseText);
}).catch(status => {
console.log(new Error(status));
})
7.RAF-requestAnimationFrame-的使用
<div id="myDiv" style="background-color: lightblue;width: 0;height: 50px;line-height: 50px;">0%</div>
<button id="btn">run</button>
//显示器刷新率60Hz,so最平滑动画的最佳循环间隔为1000ms/60=16ms
//requestID = requestAnimationFrame(callback);
//requestAnimationFrame不需要设置时间间隔
//回调函数作为参数传入,会返回一个整数(定时器的编号),可以传递给cancelAnimationFrame用于取消这个函数的执行
//IE 9- 不支持该方法
var timer1 = requestAnimationFrame(function () {
});
var timer2 = requestAnimationFrame(function () {
});
var timer3 = requestAnimationFrame(function () {
});
console.log(timer1);//1
console.log(timer2);//2
console.log(timer3);//3
//cancelAnimationFrame
cancelAnimationFrame(timer1);
cancelAnimationFrame(2);
cancelAnimationFrame(3);
//兼容IE
if (!window.requestAnimationFrame) {
var lastTime = 0;
window.requestAnimationFrame = function (callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}
//示例
var myDiv = document.getElementById('myDiv');
var btn = document.getElementById('btn');
var timer;
btn.onclick = function () {
myDiv.style.width = '0';
cancelAnimationFrame(timer);
timer = requestAnimationFrame(function fn() {
if (parseInt(myDiv.style.width) < 500) {
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width) / 5 + ' %';
timer = requestAnimationFrame(fn);
} else {
cancelAnimationFrame(timer);
}
});
}
8.js怎么控制一次加载一张图片,加载完后再加载下一张(监控图片是否加载完成)
// 方法一
var obj=new Image();
obj.src="URL";
obj.onload=function(){
alert('图片的宽度为:'+obj.width+';图片的高度为:'+obj.height);
document.getElementById("mypic").innnerHTML="<img src='"+this.src+"' />";
}
// 方法二
var obj=new Image();
obj.src="URL";
obj.onreadystatechange=function(){
if(this.readyState=="complete"){
alert('图片的宽度为:'+obj.width+';图片的高度为:'+obj.height);
document.getElementById("mypic").innnerHTML="<img src='"+this.src+"' />";
}
}
9.Job queue 的执行顺序
//在Job queue中的队列分为两种类型:macro-task和microTask。
// macro-task队列包含任务: a1, a2 , a3
// micro-task队列包含任务: b1, b2 , b3
//
// 执行顺序为,首先执行marco-task队列开头的任务,也就是 a1 任务,执行完毕后,
// 在执行micro-task队列里的所有任务,也就是依次执行***b1, b2 , b3***,执行完后清空micro-task中的任务,
// 接着执行marco-task中的第二个任务,依次循环。
//macro-task队列真实包含任务:
//script(主程序代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
//micro-task队列真实包含任务:
//process.nextTick, Promises, Object.observe, MutationObserver
//举例
setTimeout(function () {
console.log(1)
}, 0);
new Promise(function (resolve, reject) {
console.log(2);
resolve();
}).then(function () {
console.log(3)
}).then(function () {
console.log(4)
});
process.nextTick(function () {
console.log(5)
});
console.log(6);
//输出2,6,5,3,4,1
//这里要注意的一点在定义promise的时候,promise构造部分是同步执行的,这样问题就迎刃而解了。
// 首先分析Job queue的执行顺序:
//
// script(主程序代码)——>process.nextTick——>promise——>setTimeout
//
// I) 主体部分: 定义promise的构造部分是同步的,
// 因此先输出2 ,主体部分再输出6(同步情况下,就是严格按照定义的先后顺序)
//
// II)process.nextTick: 输出5
//
// III)promise: 这里的promise部分,严格的说其实是promise.then部分,输出的是3,4
//
// IV) setTimeout : 最后输出1
//
// 综合的执行顺序就是: 2——>6——>5——>3——>4——>1
10.原生ajax的请求过程
function getXHR() {
var xhr = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xhr = new ActiveXObject('Msxml2.XMLHTTP');//MSXML3
} catch (e) {
try {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
catch (e) {
alert('不支持');
}
}
}
return xhr;
}
var xhr = getXHR();
xhr.open('GET', url, true);//true 是否异步
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var data = xhr.responseText;
console.log(data);
}
}
}
xhr.onerror = function () {
console.log('error');
}
xhr.send();//发送请求
11.JS检测变量类型
var str = '123';
var num = 3;
console.log(typeof str); //string
console.log(typeof num); //number
console.log(typeof str === 'string'); //true
console.log(typeof(num) === 'number'); //true
console.log(str.constructor === String); //true
12.JS去除字符串空格
var str = ' asd dasd s d sad asd asc ';
//方法一:使用replace正则匹配
//去除所有空格
console.log(str.replace(/\s*/g, '')); //asddasdsdsadasdasc
//去除两头空格
console.log(str.replace(/^\s*|\s*$/g, '')); //asd dasd s d sad asd asc
//去除左空格
console.log(str.replace(/^\s*/, '')); //asd dasd s d sad asd asc
//去除右空格
console.log(str.replace(/(\s*$)/g, '')); // asd dasd s d sad asd asc
//方法二:使用str.trim();(只能去除左右空格)
console.log(str.trim()); //asd dasd s d sad asd asc
13.获取浏览器URL中查询字符串中的参数
function showWindowHref(){
// var sHref = window.location.href;
var sHref = 'https://www.baidu.com/s?ie=UTF-8&wd=baidu';
var args = sHref.split('?');
if(args[0] == sHref){
return "";
}
var arr = args[1].split('&');
var obj = {};
for(var i = 0;i< arr.length;i++){
var arg = arr[i].split('=');
obj[arg[0]] = arg[1];
}
return obj;
}
var href = showWindowHref(); // obj
console.log(href); Object ie: "UTF-8"wd: "baidu"__proto__: Object
14.JS字符串操作函数
var str = 'das12n12n 12 21n';
var str1 = 'Hello World!', str2 = 'My name is 张三.', str3 = 'I am very Happy.';
//stringObject.concat(str,...) 连接字符串
console.log(str1.concat(str2, str3)); //Hello World!My name is 张三.I am very Happy.
//stringObject.indexOf(searchvalue,fromindex) 方法可返回某个指定的字符串值在字符串中首次出现的位置。规定在字符串中开始检索的位置
//不存在该字符串返回 -1;
console.log(str1.indexOf('Hello')); //0
console.log(str1.indexOf('HELLO')); //-1
//stringObject.lastIndexOf(searchvalue,fromindex) 方法可返回某个指定的字符串值在字符串中最后出现的位置。规定在字符串中开始检索的位置
//不存在该字符串返回 -1;
console.log(str1.lastIndexOf('l')); //9
console.log(str1.lastIndexOf('0')); //-1
//stringObject.charAt() 返回指定位置的字符串
console.log(str1.charAt(1)); //e
//stringObject.match() 检查一个字符串是否匹配一个正则表达式
console.log(str.match(/\d+/g));
//stringObject.substr(start,length) 在字符串中抽取从 start: 下标开始的指定数目的字符 start-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推
console.log(str1.substr(0, 5)); //Hello
console.log(str1.substr(-6, 5)); //World
// stringObject.substring(start,stop) 用于提取字符串中介于两个指定下标之间的字符
console.log(str1.substring(0, 11)); //Hello World
console.log(str1.substring(0)); //Hello World!
//stringObject.slice 提取字符串的一部分,并返回一个新字符串。
console.log(str1.slice(0, 12)); //Hello World!
console.log(str1.slice(0)); //Hello World!
//replace() – 用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。
console.log(str1.replace(/\s*/g, '_'));
//search() – 执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。
console.log(str1.search('H')); //0
console.log(str1.search('ooo')); //-1
//split() – 通过将字符串划分成子串,将一个字符串做成一个字符串数组。
console.log(str1.split());
var arr1 = str1.split();
var arr2 = str2.split();
var arr = arr1.concat(arr2);
console.log(arr); //["Hello World!", "My name is 张三."]
console.log(arr[1]); //My name is 张三.
//toLowerCase() – 将整个字符串转成小写字母
//toUpperCase() – 将整个字符串转成大写字母
15.JS创建、添加、移除、移动、复制、创建和查找节点
<div id="div1"></div>
<button id="btn1">add</button>
<button id="btn2">remove</button>
<button id="btn3">replace</button>
var div1 = document.querySelector('#div1');
var btn1 = document.querySelector('#btn1');
var btn2 = document.querySelector('#btn2');
var btn3 = document.querySelector('#btn3');
//创建新节点
//创建一个DOM片段
var node1 = document.createDocumentFragment('<h1>标题1</h1>');
var browsers = ['Firefox', 'Chrome', 'Opera',
'Safari', 'Internet Explorer'];
browsers.forEach(function (browser) {
var li = document.createElement('li');
li.textContent = browser;
node1.appendChild(li);
});
//创建一个具体元素
var node2 = document.createElement('p');
//创建一个文本节点
var node3 = document.createTextNode('这是一个文本节点;HTML由元素节点和文本节点构成');
node2.appendChild(node3);
// 添加、移除、替换、插入
//添加
btn1.onclick = function () {
div1.appendChild(node1);
div1.appendChild(node2);
};
//移除
btn2.onclick = function () {
div1.removeChild(node2);
}
//替换 replaceChild(new, old);
btn3.onclick = function () {
var node3 = document.createElement('h1');
node3.innerText = '标题';
div1.replaceChild(node3, node2);
}
//插入 inertBefore();inertAfter();
//查找
document.getElementById();
document.getElementsByName();
document.getElementsByTagName();
16.JS的各种位置
//clientHeight表示的是可视区域的高度,不包含border和滚动条(css height + css padding)
console.log('clientHeight:'+document.getElementById('div').clientHeight);
//Element.scrollHeight 这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容
console.log('scrollHeight:'+document.getElementById('div').scrollHeight);
//HTMLElement.offsetHeight 是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数
console.log('offsetHeight:'+document.getElementById('div').offsetHeight);
//clientTop一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。clientTop 是只读的
console.log('clientTop:'+document.getElementById('div').clientTop);
//Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数。
console.log('scrollTop:'+document.documentElement.scrollTop);
//HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部的距离
console.log('offsetTop:'+document.getElementById('div').offsetTop);
17.内存泄漏问题
// 定义和用法
// 内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。
// 浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。
// 内存泄漏的几种情况
// 1.当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
//实例如下
var btn = document.getElementById("myBtn");
// btn.onclick = function () {
// document.getElementById("myDiv").innerHTML = "Processing...";
// }
// 解决方法
btn.onclick = function () {
/******************
* btn.onclick = null;
*
*/
btn.onclick = null;
document.getElementById("myDiv").innerHTML = "Processing...";
}
//2.由于是函数内定义函数,并且内部函数--事件回调的引用外暴了,形成了闭包。闭包可以维持函数内局部变量,使其得不到释放。
//实例如下
function bindEvent() {
var obj = document.createElement('XXX');
obj.onclick = function () {
// Even if it is a empty function
};
}
//解决方法
function bindEvent() {
var obj = document.createElement('XXX');
obj.onclick = function () {
// Even if it is a empty function
};
obj = null;
}
18.JS面向对象中继承的实现(ES5和ES6)
//ES5:寄生组合式继承:通过借用构造函数来继承属性和原型链来实现子继承父。
function Animal(name) {
this.name = name || 'Animal';
this.sleep = function () {
console.log('(Animal sleep) -> ' + this.name);
}
}
Animal.prototype.eat = function (food) {
console.log('(Animal eat) -> ' + food + ' -> ' + this.name);
}
function Cat(name) {
Animal.call(this);
this.name = name || 'Cat';
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.eatFish = function (num) {
num = num || 'a little';
console.log('(Cat eatFish) -> ' + this.name + ' -> ' + num);
};
// Object.create()的polyfill
/*
function pureObject(o){
//定义了一个临时构造函数
function F() {}
//将这个临时构造函数的原型指向了传入进来的对象。
F.prototype = obj;
//返回这个构造函数的一个实例。该实例拥有obj的所有属性和方法。
//因为该实例的原型是obj对象。
return new F();
}
*/
let animal = new Animal('Dog');
console.log(animal.name); //Dog
animal.sleep(); //(Animal sleep) -> Dog
animal.eat('Meat'); //(Animal eat) -> Meat -> Dog
console.log('-----------'); //-----------
let cat = new Cat('Big Cat');
console.log(cat.name); //Big Cat
cat.sleep(); //(Animal sleep) -> Big Cat
cat.eat('Small Fish'); //(Animal eat) -> Small Fish -> Big Cat
cat.eatFish('more'); //(Cat eatFish) -> Big Cat -> more
// ES6
class Animal {
constructor(name) {
this.name = name || 'Animal';
this.sleep = function () {
console.log('(Animal sleep) -> ' + this.name);
}
}
eat(food) {
console.log('(Animal eat) -> ' + food + ' -> ' + this.name);
}
}
class Cat extends Animal {
constructor(name) {
super(name || 'Cat');
}
eatFish(num) {
num = num || 'a little';
console.log('(Cat eatFish) -> ' + this.name + ' -> ' + num);
}
//重新声明父类同名方法会覆盖,ES5直接在原型链上操作
eat(food) {
console.log('(重写的)(Animal eat) -> ' + food + ' -> ' + this.name);
}
}
let animal = new Animal('Dog');
console.log(animal.name); //Dog
animal.sleep(); //(Animal sleep) -> Dog
animal.eat('Meat'); //(Animal eat) -> Meat -> Dog
console.log('-----------'); //-----------
let cat = new Cat('Big Cat');
console.log(cat.name); //Big Cat
cat.sleep(); //(Animal sleep) -> Big Cat
cat.eat('Small Fish'); //(重写的)(Animal eat) -> Small Fish -> Big Cat
cat.eatFish('more'); //(Cat eatFish) -> Big Cat -> more
19.JS Array对象
var arr1 = [1, 2, 3, 41, 4, 3, 312, 3, 123];
console.log(arr1.constructor);
console.dir(arr1.constructor);
// Array对象方法
// join() 用于把数组中的所有元素放入一个字符串。
var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"
console.log(arr.join(".")); //George.John.Thomas
//pop() 删除并返回数组的最后一个元素
console.log(arr1.pop()); //123
//shift() 删除并返回数组的第一个元素
// push() 向数组的末尾添加一个或更多元素,并返回新的长度。
var arrPush = ['first', 'second'];
console.log(arrPush.push('third', 'fourth'));
console.log(arrPush); //(4) ["first", "second", "third", "fourth"]
// unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
// reverse() 颠倒数组中元素的顺序
console.log(arrPush.reverse()); //(4) ["fourth", "third", "second", "first"]
// splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目
// arrayObject.splice(index,howmany,item1,.....,itemX)
// index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
// howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
// item1, ..., itemX 可选。向数组添加的新项目。
// toSource() 返回该对象的源代码。
// 只有 Gecko 核心的浏览器(比如 Firefox)支持该方法,也就是说 IE、Safari、Chrome、Opera 等浏览器均不支持该方法。
function employee(name, job, born) {
this.name = name;
this.job = job;
this.born = born;
}
var bill = new employee("Bill Gates", "Engineer", 1985);
console.log(bill.toSource());
// toString() 把数组转换为字符串,并返回结果。
// toLocaleString() 把数组转换为本地数组,并返回结果。
// valueOf() 返回数组对象的原始值
20.数组去重的几种方法
var arr = [12, 1, 1, 1, 3, 1, 3, 12, 3, 14, 2, 45, 2, 34, 21, 3, 12, 3, 21312, 3];
var obj = {};
var tmp = [];//去重后
var rec = [];//重复的
//方法一
for (let i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) {
obj[arr[i]] = 1;
tmp.push(arr[i]);
} else {
rec.push(arr[i]);
}
}
console.log(tmp);
// 0: 12
// 1: 1
// 2: 3
// 3: 14
// 4: 2
// 5: 45
// 6: 34
// 7: 21
// 8: 21312
console.log(rec);
// 0: 1
// 1: 1
// 2: 1
// 3: 3
// 4: 12
// 5: 3
// 6: 2
// 7: 3
// 8: 12
// 9: 3
// 10: 3
// 方法二
var tmp2 = [];
var rec2 = [];
for (let i = 0; i < arr.length; i++) {
if (tmp2.indexOf(arr[i]) < 0) {
tmp2.push(arr[i]);
}
else {
rec2.push(arr[i]);
}
}
console.log(tmp2);
// 0: 12
// 1: 1
// 2: 3
// 3: 14
// 4: 2
// 5: 45
// 6: 34
// 7: 21
// 8: 21312
console.log(rec2);
// 0: 1
// 1: 1
// 2: 1
// 3: 3
// 4: 12
// 5: 3
// 6: 2
// 7: 3
// 8: 12
// 9: 3
// 10: 3
//方法3
var tmp3 = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;
});
console.log(tmp3);
// 0: 12
// 1: 1
// 2: 3
// 3: 14
// 4: 2
// 5: 45
// 6: 34
// 7: 21
// 8: 21312
21.防抖
<input type="text" id="text">
const text = document.getElementById('text');
text.oninput = debounce(search, 500);
let flag = 0;
function search() {
flag++;
console.log(`发起请求${flag}次`);
}
function debounce(fn, delay) {
var timer = null;
return function () {
// 通过 ‘this’ 和 ‘arguments’ 获取函数的作用域和变量
var context = this;
var args = arguments;
// 清理掉正在执行的函数,并重新执行
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
}
}
// function debounce(fn, delay) {
// let timer
// return function (...args) {
// if (timer) clearTimeout(timer)
// timer = setTimeout(() => {
// fn.apply(this, args)
// }, delay)
// }
// }
二、HTML和CSS部分
1.清除浮动的几种方式
/* 最好方法使用:after */
div:after{
content: '';
clear: both;
display: block;
width: 0;
height: 0;
}
/* 或者新建一个空元素来清除浮动 */
.clear{
clear: both;
height: 0;
line-height: 0;
font-size: 0;
}
/* 给父元素增加overflow属性 */
.over-flow{
over-flow: auto;
zoom: 1; /* 触发IE hasLayout, 处理兼容性问题*/
}
2.引入样式link与import区别
<!--link方式-->
<link rel="stylesheet" type="text/css" href="style.css">
<!--import方式-->
<style type="text/css">
@import url(style01.css);
</style>
-
区别1:link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
-
区别2:link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
-
区别3:link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
-
区别4:ink支持使用Javascript控制DOM去改变样式;而@import不支持。
3.CSS画三角形
div{
width: 0;
height: 0;
border: 100px solid transparent;
border-bottom-color: red;/*下边框,向上的三角形*/
}
4.不使用border新建一个一像素的直线
<div style="height:1px; background-color: red; overflow: hidden; width: 100%"></div>
5.HTML5新特性
<li>语义化标签 nav header footer section aside</li>
<li>绘图的canvas</li>
<li>媒体的video和audio</li>
<li>localStorage与sessionStorage</li>
<li>表单控件:calendar、date、time、email、url、search</li>
<br/>
<h3>html5 与 html可以使用标签或者doctype区分</h3>
<br>
<h3>语义化使HTML结构更清晰,便于浏览器解析,利于SEO搜索,使代码更好理解,便于维护</h3>
6.CSS3动画
/*2D转换 transform*/
transform: translate(-20px, -20px); /*坐标内移动*/
transform: rotate(45deg); /*旋转*/
transform: scale(0.8); /*缩放*//*给定的宽度(X 轴)和高度(Y 轴)参数。transform: scale(2,4);*/
transform: skew(0, 20deg); /*倾斜*/
transform: matrix(0, 0, 0, 0, 0, 0); /*把所有 2D 转换方法组合在一起,需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素*/
/*3D转换*/
transform: rotateX(120deg); /*元素围绕其 X 轴以给定的度数进行旋转*/
transform: rotateY(120deg); /*元素围绕其 Y 轴以给定的度数进行旋转*/
/*transition 过渡效果*/
transition-property: all; /*执行动画对应属性 color background*/
transition-duration: 5s; /*动画持续时间*/
transition-timing-function: linear; /*动画变化的速率 ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier */
transition-delay: 5s; /*延迟多久开始动画*/
transition: all 5s linear 5s;
/*animation 动画*/
animation-name: name;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 5s;
animation-iteration-count: infinite; /*指定元素播放动画的循环次数 infinite | <number>*/
animation-direction: normal; /*指定元素动画播放的方向,其只有两个值,默认值为normal,如果设置为normal时,动画的每次循环都是向前播放;另一个值是alternate,他的作用是,动画播放在第偶数次向前播放,第奇数次向反方向播放。*/
animation-play-state: paused; /*控制元素动画的播放状态*/
animation: name 5s linear 5s infinite normal paused;
7.CSS垂直和水平居中的几种方式
<section style="position: relative">
不固定宽高<br/>
position: absolute;<br/>
top: 50%;<br/>
left: 50%;<br/>
margin-left: -25%;<br/>
margin-top: -25%;<br/>
<style>
#div1{
width:15vw;
height: 15vw;
overflow: hidden;
position: absolute;
top: 50%;
left: 50%;
margin-left: -25%;
margin-top: -25%;
background-color: #666666;
}
</style>
<div id="div1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores aut deserunt incidunt itaque voluptate. Enim et eum, neque nihil officia perspiciatis suscipit tenetur voluptatem voluptatum! Architecto dolorem doloribus perspiciatis vitae.
</div>
</section>
<section>
不固定宽高<br/>
transform:translate(50%,50%)
<style>
#div2{
width:15vw;
height: 15vw;
overflow: hidden;
transform:translate(50%,50%);
background-color: #666666;
}
</style>
<div id="div2">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores aut deserunt incidunt itaque voluptate. Enim et eum, neque nihil officia perspiciatis suscipit tenetur voluptatem voluptatum! Architecto dolorem doloribus perspiciatis vitae.
</div>
</section>
<section style="position: relative">
固定宽高<br/>
position: absolute;<br/>
left: 0;<br/>
top: 0;<br/>
bottom: 0;<br/>
right: 0;<br/>
margin: auto;<br/>
<style>
#div3{
width:15vw;
height: 15vw;
overflow: hidden;
background-color: #666666;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
margin: auto;
}
</style>
<div id="div3">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores aut deserunt incidunt itaque voluptate. Enim et eum, neque nihil officia perspiciatis suscipit tenetur voluptatem voluptatum! Architecto dolorem doloribus perspiciatis vitae.
</div>
</section>
<section style="display: flex;
justify-content: center;
align-items: center;">
父元素使用:<br/>
display: flex;<br/>
justify-content: center;<br/>
align-items: center;<br/>
<style>
#div4{
width:15vw;
height: 15vw;
overflow: hidden;
background-color: #666666;
}
</style>
<div id="div4">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores aut deserunt incidunt itaque voluptate. Enim et eum, neque nihil officia perspiciatis suscipit tenetur voluptatem voluptatum! Architecto dolorem doloribus perspiciatis vitae.
</div>
</section>
<section style="display: grid">
grid布局<br/>
父元素:display:grid;<br/>
子元素:align-self: center;<br/>
justify-self: center;<br/>
<style>
#div5{
width:15vw;
height: 15vw;
overflow: hidden;
background-color: #666666;
align-self: center;
justify-self: center;
}
</style>
<div id="div5">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores aut deserunt incidunt itaque voluptate. Enim et eum, neque nihil officia perspiciatis suscipit tenetur voluptatem voluptatum! Architecto dolorem doloribus perspiciatis vitae.
</div>
</section>
<section style="display: table">
父盒子宽高为100%<br/>
table布局<br/>
父元素:display:table;<br/>
子元素:display:table-cell
text-align: center;<br/>
verical-align: middle;<br/>
<style>
#div6{
width:15vw;
height: 15vw;
overflow: hidden;
background-color: #666666;
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
<div id="div6">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores aut deserunt incidunt itaque voluptate. Enim et eum, neque nihil officia perspiciatis suscipit tenetur voluptatem voluptatum! Architecto dolorem doloribus perspiciatis vitae.
</div>
</section>
三、开发性能优化
1.规避JavaScript多人开发函数重名问题
// 1.命名空间
// var MYNAMESPACE=MYNAMESPACE||{};
// 若全局空间中已有同名对象,则不覆盖该对象;否则创建一个新的命名空间。
// 举例
var MYNAMESPACE=MYNAMESPACE||{};
MYNAMESPACE.person=function (name) {
this.name=name;
};
MYNAMESPACE.person.prototype.getName=function () {
return this.name;
};
var p=new MYNAMESPACE.person('NAME');
console.log(p.getName());
// 2.封闭空间
// js中的封闭空间主要是利用了给变量加括号结果不变
// 书写方式
;(function () {
// code...
})();
// 在函数前面加分号是为了避免被别人坑,导致和别的程序猿发生肢体冲突:
// 如果别人的代码没有写分号,如果代码压缩的时候就会发生问题,
// 这个时候我们自己的代码就会拯救我们,而两个分号写在一起时是没有问题的。
// 3.JS模块化MVC(数据层、表现层、控制层)
// 4.seajs
// SeaJS是一个遵循CMD规范的JavaScript模块加载框架,可以实现JavaScript的模块化开发及加载机制
// 5.变量转化成对象的属性
var WMD={};
WMD['name']='张三';
WMD['person']=function (name) {
this.name=name;
};
WMD.person.prototype.getName=function () {
return this.name;
};
var _p=new WMD.person('Tom');
console.log(_p.getName());
console.log(WMD);
// 6.对象化
2.降低页面加载时间的方法
(1) 压缩css、js文件
// 在线压缩工具:http://tool.oschina.net/jscompress
// 由后端动态生成或工具直接生成(grunt+requirejs)
(2) 合并js、css文件,减少http请求
// 页面引入的的js,css越多的话,那么对就增加了http请求数
// 以合并JS文件为例,使用bat批处理命令
// 新建.bat批处理文件 内容 /b:固定参数
// 语法 cope 文件1.文件类型+文件2.文件类型 合并后文件名.文件类型 \b
// 例如 copy G.js+T.JS GT_bin.js /b
// 由后端动态生成或工具直接生成(grunt+requirejs)
(3) 外部js、css文件放在最底下
(4) 减少dom操作,尽可能用变量替代不必要的dom操作
3.web前端提高页面性能优化
针对HTML
1. 避免再HTML中直接写css代码。
2. 使用Viewport加速页面的渲染。
3. 使用语义化标签,减少css的代码,增加可读性和SEO。
4. 减少标签的使用,dom解析是一个大量遍历的过程,减少无必要的标签,能降低遍历的次数。
5. 避免src、href等的值为空。
6. 减少dns查询的次数。
7. 避免再HTML中直接写css代码。
针对CSS:
1.优化选择器路径:
相比于 .a .b .c{} ,更倾向于大家写.c{}
2.压缩文件
3.选择器合并
把有共同的属性内容的一系列选择器组合到一起
4.精准样式 减少不必要的属性设置
比如你只要设置{padding-left:10px}的值,那就避免{padding:0 0 0 10px}这样的写法
5.雪碧图
6.避免通配符
.a .b *{} 像这样的选择器,根据从右到左的解析顺序在解析过程中遇到通配符(*)会回去遍历整个dom
7.少用float
Float在渲染时计算量比较大,尽量减少使用。
8. 0 值去单位
9.把 CSS 放到代码页上端
针对JavaScript :
1. 脚本放到 HTML 代码页底部 (Put Scripts at the Bottom)
2. 尽可能合并script代码
3. css能干的事情,尽量不要用JavaScript来干。
4. 尽可能压缩的js文件,减少资源下载的负担
5. 尽可能避免在js中逐条操作dom样式,尽可能预定义好css样式,然后通过改变样式名来修改dom样式,这样集中式的操作能减少reflow或repaint的次数。
6. 尽可能少的在js中创建dom,而是预先埋到HTML中用display:none来隐藏,在js中按需调用,减少js对dom的暴力操作。
面向图片(Image):
1.优化图片
2 不要在 HTML 中使用缩放图片
3 使用恰当的图片格式
<!--作者:顾家进-->
<!--链接:https://juejin.im/post/5bbaa549e51d450e827b6b13-->
<!--来源:掘金-->
4.图像优化 图片格式区别
// 优化图像:
// 1、不用图片,尽量用css3代替。 比如说要实现修饰效果,如半透明、边框、圆角、阴影、渐变等,在当前主流浏览器中都可以用CSS达成。
//
// 2、 使用矢量图SVG替代位图。对于绝大多数图案、图标等,矢量图更小,且可缩放而无需生成多套图。现在主流浏览器都支持SVG了,所以可放心使用!
//
// 3.、使用恰当的图片格式。
// 我们常见的图片格式有JPEG、GIF、PNG。
// 基本上,内容图片多为照片之类的,适用于JPEG。
// 而修饰图片通常更适合用无损压缩的PNG。
// GIF基本上除了GIF动画外不要使用。且动画的话,也更建议用video元素和视频格式,或用SVG动画取代。
//
// 4、按照HTTP协议设置合理的缓存。
//
// 5、使用字体图标webfont、CSS Sprites等。
//
// 6、用CSS或JavaScript实现预加载。
//
// 7、WebP图片格式能给前端带来的优化。WebP支持无损、有损压缩,动态、静态图片,压缩比率优于GIF、JPEG、JPEG2000、PG等格式,非常适合用于网络等图片传输。
//
// 图像格式的区别:
// 矢量图:图标字体,如 font-awesome;svg
//
// 位图:gif,jpg(jpeg),png
//
// 区别:
//
// 1、gif:是是一种无损,8位图片格式。具有支持动画,索引透明,压缩等特性。适用于做色彩简单(色调少)的图片,如logo,各种小图标icons等。
//
// 2、JPEG格式是一种大小与质量相平衡的压缩图片格式。适用于允许轻微失真的色彩丰富的照片,不适合做色彩简单(色调少)的图片,如logo,各种小图标icons等。
//
// 3、png:PNG可以细分为三种格式:PNG8,PNG24,PNG32。后面的数字代表这种PNG格式最多可以索引和存储的颜色值。
//
// 关于透明:PNG8支持索引透明和alpha透明;PNG24不支持透明;而PNG32在24位的PNG基础上增加了8位(256阶)的alpha通道透明;
//
// 优缺点:
//
// 1、能在保证最不失真的情况下尽可能压缩图像文件的大小。
//
// 2、对于需要高保真的较复杂的图像,PNG虽然能无损压缩,但图片文件较大,不适合应用在Web页面上。
5.浏览器渲染页面流程
1.解析HTML文件,创建DOM树。
浏览器通过HTMLParser(HTML解析器)根据深度遍历的原则把HTML解析成DOM Tree。
自上而下,遇到任何样式(link、style)与脚本(script)都会阻塞(外部样式不阻塞后续外部脚本的加载)。
2.解析CSS。
将CSS解析成CSS Rule Tree(CSSOM Tree)。
优先级:浏览器默认设置<用户设置<外部样式<内联样式<HTML中的style样式;
3.将CSS与DOM合并,构建渲染树(Render Tree)
根据DOM树和CSSOM树来构造render Tree。
4.布局和绘制,重绘(repaint)和重排(reflow)
layout:根据得到的render tree来计算所有节点在屏幕的位置。
paint:遍历render tree,并调用硬件图形API来绘制每个节点。
(部分未完成,待更新)