JS中的THIS彙總
THIS:當前方法執行的主體(誰執行的這個方法,那麼THIS就是誰,因此THIS和當前方法在哪建立的或者在哪執行的都沒有必然的關係)
1.給元素的某個事件綁定方法,方法中的THIS都是當前操做的元素自己
document.body.onclick = function () {
=>this:body
};
2.函數執行,看函數前面是否有點,有的話,點前面是誰THIS就是誰,沒有點,THIS是WINDOW(在JS的嚴格模式下,沒有點THIS是UNDEFINED)
let fn = function () {
console.log(this.name);
};
let obj = {
name: '哈哈',
fn: fn
};
fn();//=>this:window
obj.fn();//=>this:obj
3.構造函數執行,方法中的this通常都是當前類的實例
let Fn = function () {
this.x = 100;//=>this:f
};
let f = new Fn;
4.箭頭函數中沒有本身的THIS,THIS是上下文中的THIS
let obj = {
fn: function () {
this:obj
setTimeout(() => {
this:obj
}, 1000);
}
};
obj.fn();
5.在小括號表達式中,會影響THIS的指向
let obj = {
fn: function () {
console.log(this);
}
};
obj.fn();//=>this:obj
;(12, obj.fn)();//=>this:window
6.使用call/apply/bind能夠改變this指向
fn.call(obj);//=>this:obj
fn.call(12);//=>this:12
fn.call();//=>this:window 非嚴格模式下call/apply/bind第一個參數不寫或者寫null和undefined,this都是window,嚴格模式下寫誰this就是誰,不寫是undefined
1. 寫出你所熟知的ES6新語法,說出它們和ES5的區別!
Es6中語法讓JS更加的嚴謹
-
let和const 與ES5的Var區別(let 不存在變量提高機制,不容許重複聲明,在全局做用域中基於Let聲明的變量不是Window的一個屬性和他沒有關係 ,Typeof未被聲明,let會造成塊級做用域(相似於私有做用域,大部分的大括號都會造成塊做用域))
-
解構賦值
-
「...」拓展、剩餘、展開運算符
-
箭頭函數(與普通函數區別:沒有arguments,可是能夠基於...arg獲取實參集合,沒有本身的this,箭頭函數中的this是上下文中的this)
-
promise(async與awit)異步處理
-
對於插件通常用到class類來封裝(ES中建立類的)
-
interator(for of 循環)
-
ES6中字符串模板
-
map/set
2. 請說出你對 「重排和重繪讀寫分離」 的理解! 重排(就是迴流)
思路:1.首先說出什麼是重排和重繪
2.突出他們耗性能
3.突出本身寫項目的時候重點注意了這些事情,以及本身的解決方案(說一下解決原理 )
瀏覽器渲染一個頁面的時候是按照「先建立DOM樹-》在加載CSS-》生成渲染樹render tree->把渲染樹交給瀏覽器進行繪製」,若是後期咱們修改了元素的樣式(可是沒有修改大小和位置),瀏覽器會把當前元素從新生成渲染 樹,這個機制是重繪,介是一旦元素的位置或大小等發生改變,瀏覽器就要從DOM權從新計算渲染,這個機制是迴流(重排),不管是重排仍是重繪都很是 的耗性能。
在個人之前的項目中,我特地的重視了這個問題,儘可能減小操做Dom引起的迴流和重繪問題,經常使用的解決方案:
1. 須要動態向頁面追加元素的時候,基於文檔碎片或者先把須要增長的全部元素拼接成字符串,最後統一進行增長
-
讀寫分離:把統一修改樣式都放在一塊兒執行,新版瀏覽器都有一個本身檢測的機制,若是發現下面緊挨着的操做也是修改元素的樣式,會把全部修改的事先存起來,直到遇到非修改樣式的操做,會把以前存儲的統一執行,引起一次迴流和重繪。
-
固然還有一些其餘的方法,這些是最常注意的,我認爲減小DOM的迴流重繪是很是 重要的性能優化手段之一
3. 寫出下面代碼運行的結果
var str='abc123',
num=parseFloat(str);
if(num===NaN){
alert(NaN);
}else if(num===123){
alert(123);
}else if(typeof num==='number'){
alert('number');
}else{
alert('str');
}
-->number
--------------------------
4. 寫出代碼執行的結果
var a='abc'+123+456;
alert(a);
-->'abc123456'
--------------------------
var b='456'-'123';
alert(b);
-->'333'
--------------------------
var c=1,
d='1';
var f=c>d?(c<d?c:d):(c==d?c:d);
alert(f);
-->'1'
--------------------------
5. 用戶暱稱規定只能是「數字、大小寫字母」組成,並且不能少於2位,也不能超過20位,寫個正則匹配這個需求
let reg=/^[0-9a-zA-Z]{2,20}$/
--------------------------
6. 談談你對面向對象的理解!
【JS自己是基於面向對象的】
JS自己是基於面向對象的編程思想開發出來的語言,咱們學習JS就是在學習JS中的類和實例,例如:數組是Array的實例、對象是Object的實例、函數是Function的實例...在這些內置類的原型上有不少的公共的屬性和方法,這些方法能夠被實例調用,咱們學習JS就是學習這些方法。。。
【面向對象其實項目的應用】
平時的業務邏輯開發,我沒有刻章使用類的方式來作,只有在一些組件或者插件封裝的時候纔會基於構造函數和原型鏈使用類和實例完成,例如:我以前封裝過一些TAB頁卡、輪播圖、模態框、表單驗證等插件,就是這樣處理的(我以前看了一些類庫和插件的源碼,也都是基於面向對象封裝的)
【面向對象中的一些語法和特色】
所謂面向對象就是基於Class或者function建立一個類,執行的時候new執行建立一個實例,這樣實例就能夠調取類上提供的方法, 想要基於面向對象進行插件封裝,必須掌握關於類的繼承、封裝和多態,封裝就是提供公共的方法、JS中沒有嚴格意義的多態,不能進行方法的重寫,經常使用的繼承方式有不少,例如:原型繼承、call繼承、寄生組合繼承、es6中的繼承等,有些方式會存在一些問題,我項目中後來都是基於Class中的Extend實現繼承的
7. 寫出代碼運行結果
var point={
x:10,
y:20,
moveTo:function(x,y){
var moveX=function(x){ this.x=x; }
var moveY=function(y){ this.y=y; }
moveX(x);
moveY(y);
}
};
point.moveTo(100,200);
console.log(point.x,point.y);
-->10 20
--------------------------
8. 分析代碼寫結果
function fun(){
this.a=10;
this.b=function(){
alert(this.a);
}
}
fun.prototype={
b:function(){
this.a=20;
alert(this.a);
},
c:function(){
this.a=30;
alert(this.a)
}
}
var my_fun=new fun();
my_fun.b();
my_fun.c();
-->10 30
--------------------------
9. 分析代碼寫結果
var n=2;
function a(){
var n=3;
function b(m){
alert(++n+m);
}
b(4);
return b;
}
var c=a(5);
c(6);
alert(n);
-->8 11 2
--------------------------
10. 談一下你對做用域鏈和原型鏈的理解
做用域鏈-》函數執行會造成一個私有的做用域,形參和在當前私有做用域中聲明的變量都是私有變量,當前的私有做用域有自我保護機制,私有變量和外界是沒有關係的,可是若是私有做用域中遇到一個非私有的變量,則向它的上級做用域找,若是還不是上級做用域私有的,則繼續向上查找,一直找到window爲止。這種變量一層向上查找的機制就是做用域鏈機制。
原型鏈--》它也是一種查找機制,實例首先在本身的私有屬性中進行屬性的查找,若是不是私有屬性,基於__proto__向所屬性的原型上進行查找,若是在__proto__找不到,則繼續基於向上查找,一直打到object.prototype爲止,例如Obj.hasOwnProperty()這裏調取hasOwnProperty()的這個屬性就是找到Obj.hasOwnProperty()才找到的
11. 實現 一個$attr(domId,name,value)遍歷id是domId的,內部屬性爲name且值爲value的元素?
let $attr = (domID, name, value) => {
//1.先獲取當前頁面中全部的標籤
let tagList = document.getElementsByTagName('*');
//2.在獲取的全部標籤中按照ID/NAME/VALUE進行篩選(數組內置方法:filter)
tagList = [].slice.call(tagList);//=>把類數組轉換爲數組
//=> tagList=[...tagList] 基於ES6中的展開運算符完成,讓TAG-LIST等於一個數組,數組中的每一項是把以前的類數組展開後獲得的
tagList = tagList.filter(item => {
//=>item.name:只有表單元素這樣才能夠獲取到值,普通元素須要基於getAttribute獲取值
// return item.id === domID && item.getAttribute('name') === name && (item.innerHTML === value || item.value === value);//=>傳統標籤獲取裏面的內容不是基於VALUE屬性,而是基於INNER-HTML/INNER-TEXT屬性完成的
return item.id === domID && item.getAttribute(name) === value;
});
return tagList;
};
console.log($attr('hobbyBox', 'hobby', 'music'));
// let ary = [12, 23, 34, 25, 36, 47];
// ary = ary.filter((item, index) => {
// return item > 20 && item < 40;//=>返回的結果是TRUE或者FALSE,返回的是TRUE會把這一項存放到新數組中(基於FILTER不會修改原有的數組,會把遍歷後符合條件的放到新數組中)
// });
//=>擴展:獲取當前頁面中全部ID重複的元素
12. 實現數組去重你都有哪些辦法?
//=>數組去重(不改變原有數組)
//1.對象鍵值對處理(推薦)
Array.prototype.myUnique = function () {
=>this:ary //咱們須要操做的數組,若是不想改變原有的數組,咱們須要把要操做的數組克隆一份如出一轍的處理,處理的都是克隆的這個數組
let _this = [...this],
obj = {};
for (let i = 0; i < _this.length; i++) {
let item = _this[i];
if (typeof obj[item] !== 'undefined') {
//=>當前迭代的這一項在數組中已經存在,咱們把這一項在數組中幹掉
// _this.splice(i, 1); [後面項移位,消耗性能]
_this[i] = _this[_this.length - 1];
_this.length--;
i--;
continue;
}
obj[item] = true;
}
obj = null;
return _this;
};
//=>雙循環(不推薦)
Array.prototype.myUnique = function () {
let _this = [...this];
for (let i = 0; i < _this.length; i++) {
let item = _this[i];
//=>每一次迭代到ITEM後,都拿其後面的內容和它進行比較(出現和當前項相同的,咱們就在數組中把其幹掉)
for (let j = i + 1; j < _this.length; j++) {
if (item === _this[j]) {
//=>刪除索引J這一項
_this[j] = _this[_this.length - 1];
_this.length--;
j--;
} } }
return _this;
};
//=>indexOf:獲取當前項在數組中第一次出現位置的索引,也能判斷是否存在這一項(不存在獲取的索引是-1),這個方法是不兼容IE6~8的
Array.prototype.myUnique = function () {
let _this = [...this];
//=>依次迭代數組中的每一項,驗證當前項在數組中是否存在(不是和整個數組比較是否存在,而是和當前項的後面項比較是否存在=>相似於雙FOR),存在把當前項幹掉
for (let i = 0; i < _this.length; i++) {
let item = _this[i],
nextAry = _this.slice(i + 1);
if (nextAry.indexOf(item) > -1) {
_this[i] = _this[_this.length - 1];
_this.length--;
i--;
} }
return _this;
};
//=>排序後相鄰去除法
//先把數組進行排序,驗證當前項和後一項是否相同,若是不相同,說明沒有重複,咱們把着於相提取出來保存便可
Array.prototype.myUnique = function () {
let _this = [],
ary = this.slice(0).sort((a, b) => a - b);
for (let i = 0; i < ary.length; i++) {
let item = ary[i],
next = ary[i + 1];
if (item !== next) {
_this.push(item);
}
}
return _this;
};
let ary = [1, 2, 3, 2, 3, 4, 3, 2, 2, 2, 2, 3, 4, 5, 6, 7, 4, 1, 3, 2];
let uniqueAry = ary.myUnique();
console.log(uniqueAry);
13. 說出你所掌握的算法
/*
* 經常使用的算法(算法是思惟模式)
* 遞歸(函數本身調用本身執行就是遞歸 (遞歸是基於條件判斷的:由於咱們不能造成死遞歸,在某個條件下咱們須要結束遞歸操做)
* 去重
* 冒泡排序
* 插入排序
* 快速排序
* 時間複雜度
* 空間複雜度
* KMP
* ...
*/
///=>數組扁平化(多維數組=>一維數組)
let ary = [1, [2, [3, [4, 5]]], [6, 7, [8, 9, [11, 12]], 10]]; //=>[1,2,3,4,5,6]
let str = JSON.stringify(ary);
//=>第一種處理
console.log(str);//=>[1,[2,[3,[4,5]]],6]
ary = str.replace(/(\[|\])/g, '').split(',');
console.log(ary);
//=>第二種處理
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);
console.log(ary);
let result = [],
fn = function (ary) {
if (ary.length === 0) return;
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (typeof item === 'object') {
fn(item);
} else {
result.push(item);
}}};
fn(ary);
console.log(result);
14. 寫出你掌握的JS繼承方式,項目中何時你用到了繼承?
* 面向對象:類的繼承封裝和多態
* [封裝] 把實現一個功能的JS代碼進行封裝,主要目的:「低耦合高內聚」
* [多態]
* 重載:方法名相同,參數的個數或者類型不一樣,此時名字相同的方法叫作方法的重載(後臺語言中的重載),JS中不存在重載的(=>根據傳遞參數的不一樣執行不一樣的方法)
* 重寫:子類重寫父類的方法
*
* [繼承]
* 子類繼承父類的屬性和方法
* 1. 原型繼承
* 2. call繼承
* 3. 寄生組合繼承
* 4. ES6中class類實現繼承
* ...
//=>原型繼承:讓子類的原型指向父類的一個實例
方式:B.prototype=new A();A的實例自己具有父類A的私有屬性和公有方法,子類B的原型指向它,那麼子類B的實例就能夠找到這些屬性方法了,
和傳統後臺語言的繼承不同,子類繼承父類,並非把父類的屬性方法克隆一份給子類的,JS中的原型繼承是讓子類和父類創建原型連接的機制,子類的實例調取父類原型上的方法,都是基於原型鏈的查找機制完成的
存在的問題:子類能夠重寫父類原型上的方法(重寫)子類和父類還有關係的
B.prototype.__proto__.getX=null; 把父類A的原型上的getX重寫爲Null,A的其它實例也會受到影響
//=>CALL繼承:把父類A作爲普通函數執行,讓A中的THIS變爲B的實例,至關於給B的實例增長一些屬性和方法(弊端:把父類A當作普通函數執行,和父類原型沒啥關係了,僅僅是把A中的私有屬性變爲子類B實例的私有屬性而已,A原型上的公有屬性方法和B及它的實例沒啥關係)
//=>寄生組合繼承:A的私有變爲B的私有,A的公有變爲B的公有
//=>ES6中的類和繼承(ES6中建立類是有本身標準語法的(這種語法建立出來的類只能NEW執行,不能當作普通函數執行))
15. JS中有一個insertBefore方法,目的是實現把新元素插入到指定元素以前,如今你實現一個 InsertAfter 方法,把新元素插入到指定元素以後!
function insertAfter(newEle,originEle){
//=>newEle:新插入的元素
//=>originEle:指定的老元素
let next=originEle.nextElementSibling(),
pra=originEle.parentNode;
if(next){
pra.insertBefore(newEle,next)
}else{
pra.appendchild(newEle)
}}
16. 英文字母漢字組成的字符串,用正則給英文單詞先後加空格
let reg=/^$/
17. jQuery的原理,怎麼擴展插件
JQ是一個JS類庫,裏面提供了不少的經常使用方法,有助於咱們快速開發,並且這些方法是兼容全部瀏覽器的(V2 / V3 不兼容低版本瀏覽器)
我以前在學習原生JS的時候,或多或少的看了一部分JQ源碼,剛畢業的時候JQ用的比較多,可是最近兩年一直都在用框架開發,JQ中經常使用的方法忘差很少了。以前看源碼的時候,發現JQ就是一個類,而$()就是建立這個類的一個實例,這個實例是基於內置方法makeArray創造的類數組
JQ提供的方法有兩部分,一部分是放到原型上的,供實例調取使用,一部分是放到對象上的,直接$.xxx調取使用,想要後期本身擴展方法(包括基於JQ寫插件),均可以基於extend這個方法向JQ中擴展
JQ中提供了動畫、事件、AJAX等經常使用的方法,我學習JQ源碼的時候比較注重裏面的一些封裝和編程的思想,例如:發佈訂閱這種設計模式我就是依據JQ的$.Callbacks學習研究的,因此學習JQ給我帶來了不少的好處...
18. 看代碼,回答問題
for(var i = 0;i<5;i++){
setTimeout(function(){
console.log(i)
},1000);
}
這段代碼輸出什麼?怎麼才能輸出01234?
19. 分析代碼寫結果
var a = {n:4};
var b = a;
b.x = a = {n: 10};
console.log(a.x);
console.log(b.x);
20. 你瞭解過閉包嗎?
var fullName='language';
var obj={
fullName:'javascript',
prop:{
getFullName:function(){
return this.fullName;
}
}
};
console.log(obj.prop.getFullName());
var test=obj.prop.getFullName;
console.log(test());
22.
let a = 3,
b = 4;
function A(a) {
A = function (b) {
alert(a + (--b));
};
alert(++a);
}
A(5);
A(6);
23.
window.val = 1;
let json = {
val: 10,
dbl: function () {
this.val *= 2;
}
};
json.dbl();
let dbl = json.dbl;
dbl();
json.dbl.call(window);
alert(window.val + json.val);
24.
(function () {
let val = 1;
let json = {
val: 10,
dbl: function () {
val *= 2;
}
};
json.dbl();
alert(json.val + val);
})();
25.
let test = (function (i) {
return function () {
alert(i *= 2);
}
})(2);
test(5);
26.
let n = 2,
fn = () => {
this.n *= 3;
n++;
return m=>console.log((++n)+m);
};
var f = fn(4);
f(5);
fn(4)(5);
f(6);
console.log(n);
27. 忽略報錯阻礙代碼的執行
let Fn = function (x = 0, y = 0) {
this.x = x;
this.y = y;
this.getX = function () {
console.log(this.x);
}
};
Fn.prototype.getX = function () {
console.log(this.x);
};
let f1 = new Fn;
Fn.prototype = {
getY: function () {
console.log(this.y);
}
};
let f2 = new Fn(1, 2);
console.log(f1.constructor===f2.constructor);
f1.getX();
f1.getY();
f1.__proto__.getX();
f1.__proto__.getY();
f2.getX();
f2.getY();
f2.__proto__.getX();
f2.__proto__.getY();
28. 寫出輸出結果,說出緣由
let fn1=function(){alert(1)},
fn2=function(){alert(2)};
fn1.call(fn2);
fn1.call.call(fn2);
29. 以下一個字符串 「54389」,要求將字符串中的阿拉伯數字替換成咱們的中文大寫數字」伍肆叄捌玖」,請使用正則的方式進行處理
let str='54389';
let ary=['零','壹','貳','叄','肆','伍','陸','染','捌','玖'];
str=str.replace('/\d/g',item=>{
//=>item =>arg[0] =>正則每一次捕獲的內容 5/4/3/8/9
// //=>把捕獲的數字作爲索引,到ARY中找到對應的漢字,用找到的結果替換當前捕獲的內容
return ary[item];
})
30. 在javascript對象上定義一個repeatify函數,這個函數接受一個整數參數,來明確子字符串須要重複幾回,這個函數要求字符串重複指定的次數,好比:’abc’.repeatify(3);//」abcabcabc」
String.prototype.repeatify=function repeatify(n=1){
// =>this: 須要處理的字符串
let result='';
for(let i=0; i< n; i++){
result+=this;
}
return result;
}
31. var str='hello<img src="haha.png" alt="哈哈"/>world';正確匹配輸出’hello[哈哈]world’
var str='hello<img src="haha.png" alt="哈哈"/>world';
let reg=/<img [^<>]*alt=(?:"|')(.*)(?:"|')\/>/g
str=str.replace(reg,(...arg)=>{
return `[&{arg[1]}]`;
})
32. 一個url 後面好多key-value 如localhost?key=val&key2=val2&key3=val3 封裝一個函數 getParam(‘key’) 經過key得到相應等號後面的值.
let getParam=function (attr){
let str='localhost?key=val&key2=val2&key3=val3',
obj={},
reg=/([^?&=#]+)=([^?&=#]+)/g;
str.replace(reg,(...arg)=>{
let [,key,value]=arg;
obj[key]=value;
})
return obj[attr];
}
getParam(‘key’)
/*
NODE中提供一個URL.PARSE方法,這個方法能夠把一個URL地址中的每一部分都捕獲到,最後存儲成爲一個對象
let url = require('url');
{
protocol: 'http:',
port: '80',
hash: '#teatcher',
search: '?name=xxx&age=9',
query: { name: 'xxx', age: '9' },
pathname: '/stu/index.html'
}
*/
/*
let link = document.createElement('a');
link.href = str;
let {hash, hostname, pathname, protocol, search, port} = link;
//=>端口號:若是沒有值,則使用默認端口(HTTP:80 HTTPS:443 FTP:21)
if (!port) {
switch (protocol) {
case 'https:':
port = 443;
break;
case 'ftp':
port = 21;
break;
default:
port = 80;
}
}
//=>QUERY
let query = {};
if (search) {//=>search:"?name=xxx&age=9"
search.replace(/([^?=&]+)=([^?=&]+)/g, (...arg) => {
let [, key, value] = arg;
query[key] = value;
});
}
let result = {
protocol,//=>protocol:protocol
hostname,
port,
pathname,
search,
hash,
query
};
console.log(result);
*/
33. call、apply、bind的區別
Call apply的做用
1.改變函數中的THIS(而且讓函數執行)
2.能夠基於CALL讓類數組借用數組原型上的方法(例如:借用SLICE實現把類數組轉換爲數組)
3.能夠基於CALL實現繼承
4.能夠基於APPLY獲取數組中的最大值和最小值
都是用來改變this的指向的
call 傳遞的是數值
apply 傳遞的是數組
call、apply對函數直接調用
Bind 執行方法返回一個函數
//3.基於APPLY
// console.log(Math.max.apply(null, ary));
//4.基於ES6的展開運算符
// console.log(Math.max(...ary));
34. 有兩個升序數組,而後將他們合爲 一個數組並進行升序排序?
let ary1 = [1, 2, 3, 4, 5],
ary2 = [2, 3, 4, 5, 6];
// let ary = ary1.concat(ary2).sort((a, b) => a - b);
let ary = [...ary1, ...ary2].sort((a, b) => a - b);
console.log(ary);
35. 瀑布流的實現原理
1.並排排列三列,三列沒有具體的高度,靠內容撐開
2.經過API接口地址,基於AJAX,從服務器端獲取數據,拿出數據的前三項依次插入到三列中(數據綁定)
3.計算目前三列的高度,按照高度由小到大把三列進行排序,再次拿出獲取數據中的三條,按照排好序的LI依次插入......一直基於這個規律插入完成便可
4.當用戶下拉到頁面底部,加載更多的數據便可
36. 圖片延遲加載怎麼實現
/*
* 圖片延遲加載(圖片懶加載)
* 前端性能優化的重要手段之一,開始加載頁面的時候,並無加載真實的圖片,當頁面結構和數據都呈現完成後,在加載真實的圖片
*
* 1.在結構上,咱們把IMG圖片放到一個DIV盒子中,開始的時候圖片的SRC(SRC中有地址就按照地址加載圖片)爲空,咱們把圖片的地址存放到自定義屬性DATA-SRC中(此位置不展現真實的圖片),咱們給圖片所在的盒子設置一個默認的背景圖片佔位(要求這張圖片越小越好 1KB)
* 2.在JS中,當監聽到頁面中的結構和數據都加載完成後(或者設置一個間隔時間),開始把DATA-SRC自定義屬性中存儲的真實圖片地址賦值給IMG的SRC屬性(瀏覽器此時開始加載真實的圖片 =>爲了防止圖片地址不存在致使的404錯誤,咱們在賦值給圖片的SRC屬性時,每每都會驗證一下圖片是否存在)
*/
//=>當頁面加載完成(結構、數據、DOM等都加載完成)
// window.onload=function(){}
//=>也能夠設置一個定時器,間隔多長時間後在加載真實圖片(定時器是異步的,因此定時器能執行,也表明頁面加載完成)
// setTimeout(function(){},100);
var imgBox = document.getElementById('imgBox'),
pageImg = imgBox.getElementsByTagName('img')[0];
setTimeout(function () {
//=>加載真實圖片
var trueImg = pageImg.getAttribute('data-src');
//=>建立一個臨時的IMG來驗證
// var tempImg = document.createElement('img');
var tempImg = new Image();
tempImg.onload = function () {
//=>圖片加載成功觸發這個事件
pageImg.src = trueImg;
pageImg.style.display = 'block';
tempImg = null;
};
tempImg.src = trueImg;//=>在部分IE瀏覽器中只有把SRC賦值放到ONLOAD下面才能起到做用
//=>這樣作很差:若是圖片不存在,在部分瀏覽器中,頁面中的IMG部分顯示的是一個叉叉,很差看(咱們最好在賦值給頁面的SRC屬性的時候,先驗證一下圖片是否存在,存在咱們在賦值)
// pageImg.src = trueImg;
// pageImg.style.display = 'block';
}, 1000);
/// 圖片延遲加載
$(function () {
let $container = $('.container'),
$imgList = null;
//1.先綁定數據
~function () {
let str = ``;
for (let i = 0; i < 100; i++) {
let ran = Math.round(Math.random() * 3 + 1);
str += `<div class="imgBox">
<img src="" alt="" data-src="img/banner${ran}.jpg">
</div>`;
}
$container.html(str);
$imgList = $container.find('img');
}();
//2.加載真實的圖片
//=>lazyImg:單張圖片延遲加載(傳遞給我誰,我就加載誰)
let lazyImg = curImg => {
let $curImg = $(curImg),
trueImg = $curImg.attr('data-src');
let tempImg = new Image();
tempImg.onload = () => {
// $curImg.attr('src', trueImg).css({
// display: 'block'
// });
$curImg.attr('src', trueImg).stop().fadeIn(300);//=>結束當前正在運行的動畫,執行FADE-IN,讓圖片300MS內漸現出來(JQ中提供的動畫方法)
tempImg = null;
curImg.isLoad = true;//=>圖片加載成功後,設置一個自定義屬性存儲當前圖片已經加載了,後期不須要重複的加載
};
tempImg.src = trueImg;
};
//=>computedImg:計算哪張圖片能夠加載了
let computedImg = () => {
//=>觀察全部圖片中誰能加載了,就執行LAZY-IMG讓其加載便可
$imgList.each((index, curImg) => {
//=>A:當前圖片所在盒子的底邊距離BODY偏移
//=>B:當前瀏覽器底邊距離BODY偏移
let $curImg = $(curImg),
$imgBox = $curImg.parent(),
A = $imgBox.offset().top + $imgBox.outerHeight(),
B = document.documentElement.scrollTop + document.documentElement.clientHeight;
if (A <= B) {
//=>表明圖片所在盒子呈如今視野中,開始加載真實的圖片
if (curImg.isLoad) {
//=>當前圖片若是已經加載過了,不在重複的加載
return;
}
lazyImg(curImg);
}
});
};
$(window).on('load scroll', computedImg);//=>LOAD和SCROLL的時候作相同的事情(JQ中的事件綁定特色)
});
37. 寫出完整的驗證函數
1)長度不能小於6位
2)首字母必須是字母
3)合法字符只能是數字、字母、下劃線
let reg = /^[a-zA-Z]\w{5,}$/;
38. 使用jquery實現點擊按鈕彈出一個對話框(對話框在整個頁面正中間,而且最初頁面中沒有任何的HTML標籤)?
$(function () {
//=>當頁面結構加載完成執行函數
$('#link').on('click', function () {
$('<div class="center"></div>').appendTo(document.body);
});
});
39. 怎麼避免全局變量的污染?
閉包或是單列模式
40.
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
41.在函數式編程當中有一個很重要的概念就是函數組合,實際上就是把處理數據的函數像管道同樣鏈接起來,而後讓數據穿過管道獲得最終的結果。例如:
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
div2(mul3(add1(add1(0)))) //=>3
而這樣的寫法可讀性明顯太差了。咱們能夠構建一個 compose 函數,它接受任意多個函數做爲參數(這些函數都只接受一個參數),而後 compose 返回的也是一個函數,達到如下的效果:
const operate = compose(div2, mul3, add1, add1)
operate(0) //=>至關於div2(mul3(add1(add1(0))))
operate(2) //=>至關於div2(mul3(add1(add1(2))))
簡而言之:compose 能夠把相似於 f(g(h(x))) 這種寫法簡化成 compose(f, g, h)(x)。請你完成 compose 函數的編寫。
額外挑戰:你能經過 1~2 行代碼實現 compose 嗎。
=>把數組拼成字符串,把字符串EVAL了便可
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
[div2, mul3, add1, add1]
'div2(mul3(add1(add1(0))))'
const compose = (...arg) => {//=>不銷燬的棧
//=>arg:[div2, mul3, add1, add1]
return val => {
//=>fn(0):執行的是小函數,val=0
let str = '';
arg.forEach(item => (str += item.name + ','));
str = str.replace(/,/g, '(');
str += val;
arg.forEach(item => (str += ')'));
return eval(str);
}
};
let fn = compose(div2, mul3, add1, add1);
console.log(fn(0));//=>div2(mul3(add1(add1(0))))
// * 柯理化函數編程思想
* 1.執行一個方法,傳遞一些參數進去,首先造成一個不銷燬的棧,把傳遞的這些值存儲起來(沒有當即使用,屬於預先存儲一下)
* 2.返回一個小函數給棧外面
* 3.當執行返回的小函數時候,把以前第一步預先存儲的信息拿過來使用(做用域鏈、閉包等機制完成的)
*
* 咱們把JS中基於閉包實現的預先存儲的思想成爲 「柯理化函數思想」
*/
const compose = (...arg) => {
//=>arg:[div2, mul3, add1, add1]
arg = arg.reverse();//=>[add1, add1,mul3,div2]
return val => {
//=>val:0
arg.forEach(item => {
val = item(val);
//第一次 add1(0) =>1 =>val=1
//第二次 add1(1) =>2 =>val=2
//第三次 mul3(2) ...
});
return val;
}
};
let fn = compose(div2, mul3, add1, add1);
console.log(fn(0));//=>div2(mul3(add1(add1(0))))
42. 點擊每個li能夠建立出對應的對象(能夠不兼容低版本瀏覽器)
[結構]
<ul>
</ul>
點擊第一個LI建立對象:
{
index:1,
name:'xxx',
}
一樣點擊第二個LI建立對象
{
index:2,
name:'sss,
}
....
$('#nav>li>a').on('click', function (ev) {
//=>this:當前點擊的A
//=>$(this):當前點擊的A(JQ對象)
//=>阻止點擊A標籤頁面跳轉的行爲
ev.preventDefault();
//=>準備數據
let $this = $(this),
$p = $this.parent();
let obj = {
index: $p.index() + 1,
name: $this.text(),
link: $this.attr('href')
};
console.log(obj);
});
43. 分析此函數的做用,補全1/2處的代碼
44. 獲取數據中的最大值
let ary = [12, 23, 24, 35, 16];
=>獲取數組中的最大值
1.數組先排序,而後獲取第一個和最後一個就是最大最小值0
console.log(ary.sort((a, b) => b - a)[0]);
2.假設法:假設第一個是最大的,讓其和後面每一項進行比較,若是當前項大於假設的值,修改假設的值
let max = ary[0];
ary.slice(1).forEach(item => {
item > max ? max = item : null;
});
console.log(max);
45. 編寫一個函數,把一個列表中的每一項反序
<ul id='target'>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
46. 編寫一個函數實現數組扁平化
let ary = [1,[2,[3,[4,5]]],6]; //=>[1,2,3,4,5,6]
第一種
let str=JOSN.stringify(ary)
ary=str.replace(/(\[|\])/,"").split(',');
第二種
str=str.replace(/(\[|\])/,"")
str='['+str+']'
ary=JOSON.parse(str);
第三種
let result=[]
let fn=function(ary){
if(ary.length==0) return;
for(let i=0;i<ary.length;i++)
{
let item=ary[i];
if(typeOf item==='Object')
{
fn(item)
}else{
result.push(item)
}
}
}
47. 網頁中實現一個計算,計算當年還剩多少時間的倒數計時程序,要求網頁上顯示 「xxxx年還剩xx天xx時xx分xx秒」;(獲取當前時間採用new Data()便可)
48. offsetHeight/clientHeight/scrollHeight的區別
clientHeight:可視區域的高度
offsetHeight當前對象元素的高度
scrollHeight對象元素的真實高度
49. 獲取字符串中出現次數最多的字符及出現的次數
let str = 'zhufengpeixunzhouxiaotian';
/*
* 思路一:獲取字符串中的每個字母,而後以對象鍵值對的方式存儲起來(屬性名是字符,屬性值是出現的次數)
*/
//1.獲取每個字符出現的次數,以及出現的最大次數
let obj = {},
max = 1,
result = [];
str.replace(/./g, char => {
if (obj.hasOwnProperty(char)) {
obj[char]++;
if (obj[char] > max) {
max = obj[char];
}
return;
}
obj[char] = 1;
});
//2.獲取和MAX相匹配次數的字符
for (let char in obj) {
if (obj.hasOwnProperty(char)) {
if (obj[char] === max) {
result.push(char);
}
}
}
console.log(`最多出現的次數是:${max} 次,對應的字符有:${result}`);
//
// /*
// * 思路二:先把字符串中的每個字符變爲數組中的每一項,給數組排序,在變爲字符串(相同的字符挨着),在基於正則捕獲替換
// */
let max = 1;
str = str.split('').sort().join('');//=>'aaeefghhiiinnnooptuuuxxzz'
str = str.replace(/(.)\1*/g, (...arg) => {
let [value, char] = arg,
len = value.length;
len > max ? max = len : null;
return `${char}{${len}}`;
});
// console.log(str);//=>'a{2}e{2}f{1}g{1}h{2}i{3}n{3}o{2}p{1}t{1}u{3}x{2}z{2}'
// let reg =/([^\d{}])\{"+max+"\}/g;//=>字面建立正則的方式,正則中的每個字符都是元字符,不能實現把一個變量的值做爲正則一部分的需求
let reg = new RegExp('([^\\d{}])\\{' + max + '\\}', 'g');
str.replace(reg, (...arg) => {
console.log(arg[1]);
});
50. 完成如圖所示的文字橫向無縫銜接滾動的「跑馬燈」效果