第一題
function fun(){
for(var i=0,arr=[];i<3;i++){
arr[i]=function(){
console.log(i)
}
}
return arr;
}
var funs=fun();
funs[0]();//3
funs[1]();//3
funs[2]();//3
第二題
去掉數組中非數字字符,並給每個數字加+1
var arr=[1,2,3,“a”,4,“b”]
for(var i=arr.length-1;i>=0;i--){
if(typeof arr[i]==="number"){
arr[i]++ ;
}else{
arr.splice(i,1);
}
}
第三題
在兩個排好序的數組中,高效率的找出相同的元素,放入新數組
var arr1=[1,3,7,9,12,37,45]
var arr2=[2,4,9,13,45,88,92]
for(var i=0,j=0,arr=[];i<arr1.length&&j<arr2.length;){
if(arr1[i]>arr2[j]){
j++;
}else if(arr1[i]<arr2[j]){
i++;
}else{
arr.push(arr1[i]);
i++;
j++;
}
}
第四題
找出一個排好序的數組中,兩個元素相加和爲19的元素組合,考慮程序的執行效率
var arr=[1,2,4,6,7,11,12,15,17]
for(var i=0,j=arr.length-1;i<j;){
if(arr[i]+arr[j]<19){
i++;
}else if(arr[i]+arr[j]>19){
j--;
}else{
console.log(`${arr[i]}+${arr[j]}`);
i++;
j--;
}
}
第五題
統計一個字符串中每種字符出現的次數?出現次數最多的是哪個字?共出現幾次
for(var i=0,arr=[];i<str.length;i++){
var char=str[i];
if(arr[char]===undefined){
arr[char]=1
}else{
arr[char]++;
}
}
/* 這種方法也可以
var res=str.split("").reduce(function(prev,elem){
if(prev[elem]!==undefined){
prev[elem]++
}else{
prev[elem]=1;
}
return prev;
},{})
console.log(res);
*/
var maxChar,count=0;
for(var key in arr){
if(arr[key]>count){
maxChar=key;
count=arr[key];
}
}
第六題
[]-[], []+[] 分別輸出多少
- 法計算永遠試圖將左右兩邊的值都轉爲數字,因爲只有數字可以做減法。所以,自動調用Number()
Number([]) = 0 記住。
[]-[]等於0, []-1等於-1
+ 法則完全不同。兩個對象相加,會自動調用兩個對象的toString方法,將兩個對象轉爲字符串,然後執行字符串拼接。
[].toStirng() = “”,因爲數組沒有值,所以轉爲空字符串
[]+[],等於""+"",返回""
[1,2,3]+[4,5,6], 等於"1,2,3"+“4,5,6” 結果:
“1,2,34,5,6”
第七題
function fun(o){
o.name="西西";
o={};
o.name="小麗";
}
var obj={name:"小紅",age:11};
fun(obj);
console.log(obj);
> {name: "西西", age: 11}
第八題
深克隆
function clone(obj){
var newObj={};
if(obj==null){
return null;
}else if({}.toString.call(obj)==="[object Array]"){
var newArr=[];
newArr=obj.slice();
return newArr;
}
for(var key in obj){
if(typeof obj[key]!=="object")//typeof null 也是object
{
newObj[key]=obj[key];
}else{
newObj[key]=clone(obj[key]);
}
}
return newObj;
}
第九題
function fun(){//媽媽
var n=999;//紅包
//共生了幾個孩子?
nAdd=function(){n++};//剖腹產
//js中一個特徵: 任何情況下給一個從未聲明過的變量賦值,不會報錯!而是自動在全局創建該變量!
return function(){//順產
console.log(n)
}
}
var getN=fun();//媽媽生了一次孩子,包了一個紅包
//getN:function(){console.log(n)}
getN();//999
nAdd();
getN();//1000
第十題
// var a=10;
// function fun(){
// var a=100;
// a+=1;
// console.log(a)
// }
// fun();//101
// console.log(a);//10
// var a=10;
// function fun(){
// a=100;
// a+=1;
// console.log(a)
// }
// fun();//101
// console.log(a);//101
var a=10;
function fun(a){//局部變量
/**只要函數自己有a,則內部一切操作與全局無關***/
a=100;
a+=1;
console.log(a)
/******************************************/
}
fun(a);//101
console.log(a);//10
第十一題
function ltrim(str){
return str.replace(/^\s+/,"")
}
function rtrim(str){
return str.replace(/\s+$/,"")
}
function trim(str){
return str.replace(/^\s+|\s+$/g,"")
}
第十二題
function fun(){console.log(1)};
fun();//2
function fun(){console.log(2)};
fun();//2
var fun=100;
fun();//報錯
> 2
> 2
>Uncaught TypeError: fun is not a function
at <anonymous>:6:1
第十三題
內置對象11種
String Boolean Number
Math Date RegExp Array
Function Error Object
Global
第十四題
如何知道現在的瀏覽器中該類型下都有哪些可用的函數?
所有類型的函數列表都保存在原型對象中:
比如: Array.prototype下包含着目前數組對象可用的所有函數
String.prototype 下包含着目前字符串對象可用的所有函數
第十五題
如果經常使用的一個函數,在原形對象中沒有,而我們又想讓所有的孩子都能用上這個函數:
自己定義該函數,並強行放入原型對象中!
比如: 經常對數組求和,但是數組暫時沒有求和的函數sum
Array.prototype.sum=function(){
… …
}
將來: arr1.sum() arr2.sum() … …
強調: 運行對象方法中應該用this指向將來調用該函數的.前的當前類型的子對象。
比如:
Array.prototype.sum=function(){
console.log(“調用了一次自己的sum函數”);
var sum=0;
for(var i=0;i<this.length;i++){
sum+=this[i]
}
return sum;
}
var arr1=[1,2,3];
var arr2=[1,2,3,4,5];
var arr3=[1,2,3,4,5,6,7];
console.log(
arr1.sum(),
arr2.sum(),
arr3.sum()
)
第十六題
自有屬性和共有屬性:
(1). 自有屬性: 直接保存在當前對象內部,僅歸當前對象自己所有的屬性
(2). 共有屬性: 保存在原型對象中,所有子對象共有的屬性
(3). 獲取屬性值: 二者沒有差別,都可用子對象打.訪問
子對象.屬性名
程序會優先在當前對象內部先找自有屬性。如果沒有想要的自有屬性,纔去父對象中查找
(4). 修改屬性值:
a. 自有屬性可用用子對象直接修改:
lilei.sage=26
b. 共有屬性不能用子對象修改,必須用原型對象本身來修改
構造函數.prototype.共有屬性=值
比如: Student.prototype.className=“初二2班”
如果強行用子對象修改共有屬性,不會報錯,而是給這個子對象悄悄添加一個自有同名屬性。從此,這個子對象,在這個屬性的使用上和其它子對象分道揚鑣
第十七題
總結: this 目前4種用法:
- obj.fun() this->obj
- fun() this->window
- new Fun() this->new(正在創建的新對象)
- Array.prototype.sum=function(){ this }
arr1.sum() this->arr1
arr2.sum() this->arr2
第十八題
function Foo(){
Foo.a=function(){
console.log(1);
}
this.a=function(){
console.log(2);
}
}
Foo.prototype.a=function(){
console.log(3);
}
Foo.a=function(){
console.log(4)
}
Foo.a();
let obj=new Foo();
obj.a();
Foo.a();
> 4
> 2
> 1
第十九題
var x=0;
var foo={
x:1,
bar:function(){
console.log(this.x);
var that=this;
return function(){
console.log(this.x);
console.log(that.x);
}
}
}
foo.bar();
foo.bar()();
> 1
> 1
> 0
> 1
第二十題
function fun(n,o){
console.log(o);
return {
fun:function(m){
return fun(m,n)
}
}
}
var a=fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
> undefined
> 0
> 0
> 0
var b=fun(0)
.fun(1)
.fun(2)
.fun(3);
> undefined
> 0
> 1
> 2
var c=fun(0).fun(1);
c.fun(2);
c.fun(3);
> undefined
> 0
> 1
> 1
第二十一題
function A(){
}
function B(){
return new A();
}
A.prototype=new A();
B.prototype=new B();
var a=new A();
var b=new B();
console.log(a.__proto__==b.__proto__);
第二十二題
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();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName(); //2
new Foo().getName();//3
new new Foo().getName();//3
第二十三題
判斷一個對象是不是數組類型,一共有幾種方法:
//不正確的方法:typeof
var n=10,str='hello',b=true,nu=null,un;
var f=function(){};
var obj1={},obj2=[1,2,3],obj3=new Date();
console.log(
typeof(n),//number
typeof(str),//string
typeof(b),//boolean
typeof(nu),//object
typeof(un),//undefined
typeof(f),//function
typeof(obj1),//object
typeof(obj2),//object
typeof(obj3)//object
)
//判斷爹:3種
//1.用__proto__獲得對象的爹,然後再和數組的爹做比較
console.log(
obj1.__proto__==Array.prototype,//false
obj2.__proto__==Array.prototype,//true
obj3.__proto__==Array.prototype //false
)
//2.因爲__proto__可能被瀏覽器禁用,所以有等效的函數來完成__proto__的任務:Object.getPrototypeOf(child)
console.log(
Object.getPrototypeOf(obj1)==Array.prototype,//false
Object.getPrototypeOf(obj2)==Array.prototype,//true
Object.getPrototypeOf(obj3)==Array.prototype//false
)
//3.還有一個更直接的函數:father.isPrototypeOf(child)
console.log(
Array.prototype.isPrototypeOf(obj1),//false
Array.prototype.isPrototypeOf(obj2),//true
Array.prototype.isPrototypeOf(obj3)//false
)
//判斷媽媽
//4.用父級原型對象中的constructor屬性
console.log(
obj1.constructor==Array,//false
obj2.constructor==Array,//true
obj3.constructor==Array//false
)
//用"child instanceof 媽媽"
console.log(
obj1 instanceof Array,//false
obj2 instanceof Array,//true
obj3 instanceof Array//false
)
//當obj1.__proto__=Array.prototype時,以上的判斷方法均失效
//輸出對象中的DNA:內部隱藏屬性class
//.call()可讓任意一個對象,搶到原本不屬於它的任何一個函數
console.log(Object.prototype.toString.call(obj1)==="[object Array]");//false
console.log(Object.prototype.toString.call(obj2)==="[object Array]");//true
console.log(Object.prototype.toString.call(obj3)==="[object Array]");//false
//ES5中新增了一個專門判斷一個對象是不是數組的函數:Array.isArray(obj)>bool
console.log(
Array.isArray(obj1),//false
Array.isArray(obj2),//true
Array.isArray(obj3)//false
)
第二十四題
var number=2;
var obj={
number:4,
fn1:(function(){
this.number*=2;
number*=2;
var number=3;
return function(){
this.number*=2;
number*=3;
console.log(number)
}
})()
}
var fn1=obj.fn1;
console.log(number);//4
fn1();//9
obj.fn1();//9
console.log(number);//8
console.log(obj.number);//16
第二十五題
console.log(window.a)輸出undefined,而console.log(a),就報錯,難道window.a是全局,a就不是全局?
答:所有全局變量一定是保存在window中的
保存在window中的變量其實有兩種訪問方式:
可以以變量語法方式: 變量名
可以以window對象的成員形式使用: window.變量
比如: var a=10;
console.log(a) //10
因爲所有沒有前綴的,js都會自動加上window.前綴——僅限於全局作用域中執行的程序
比如: alert() -> 執行時被補全爲window.alert()
再比如: parseInt() -> 執行時被補全爲window.parseInt()
console.log(window.a) //10
如果沒有聲明a,執行console.log(a)和console.log(window.a),一個報錯,一個不報錯?
語法規定:
如果直接訪問一個不存在的變量,會報錯!ReferenceError: a未定義
如果強行訪問對象中一個不存在的屬性,或訪問數組中一個不存在的位置,不報錯!而是返回undefined。
全局變量a,其實都有兩個角色,一個是單純的變量a,同時也是window對象中的成員。所以,以單獨變量方式訪問不存在的變量,則參考第一條規則,會報錯。如果以window.a成員的方式訪問變量a,永遠不會報錯!而是返回undefined。
補: var a和 window.a
1. 如果未聲明變量a,則單獨使用a會報錯,而使用window.a不報錯。
2. var a的變量不能用delete徹底刪除
而用window.a強行添加的變量,才能被delete徹底刪除
總結: 今後即使用全局變量,也要用window.變量。不要用var。因爲如果不想用了,還可以用delete徹底刪除。
第二十六題
pay=null後,爲什麼變量pay還在?
答: 給pay變量賦值爲null,只是清空變量的內容,而變量在window中不會被刪除。
如果希望徹底刪除變量: delete window.變量
但是: 刪除不掉!
原因: pay是var pay創建的。如果想用delete徹底刪除變量,創建變量時,必須用window.pay
第二十七題
console.log(a) //報錯,爲什麼不是undefine?
a=10;
console.log(a)
//因爲a=10前,沒有var。沒有var,就不是聲明語法,就不會會被聲明提前。不會被聲明提前,第一次使用時就沒有a可用,就報錯。a=10,只是一個賦值語句。賦值語句會強行創建變量a,但是,也只能在語句所在位置才創建a。因爲聲明提前規定,“”賦值留在原地“
第二十八題
爲什麼加入overflow:hidden或添加元素設置爲display:table,可防止高度坍塌?BFC?
答: Block Formatting Context
以塊的形式,格式化顯示元素的子內容
爲什麼: 啓用BFC模式都是爲了避免子元素被隱藏或遮擋。
如何啓用BFC:
浮動元素:float 除 none 以外的值。
絕對定位元素:position (absolute、fixed)。
display 爲 inline-block、table-cells、flex。
overflow 除了 visible 以外的值 (hidden、auto、scroll)
後果:
1. 父元素啓用BFC,則範圍必須包含所有子元素的範圍。
2. 平級子元素啓用BFC,則其他兄弟子元素不能覆蓋在它之上。
第二十九題
var tom='tom';
tom.money=100;
//new String(tom).money=100;
//調用後自動釋放
console.log(tom.money);//undefined
//new String(tom).money
var tom=new String("tom");
tom.monry=10;
console.log(tom.money);//10
第三十題
閉包的外層函數向外拋出內層函數共有幾種方式?
答: 3種:
1. return function(){ … }
2. 全局變量=function(){ … }
3. return [ function, …]
return { 方法: function(){ … } }
第三十一題
一共講了幾種this的情況?
答: 4種:
1. obj.fun() -> this->obj
2. fun() 或(function(){ … })() this->window
3. new fun() -> this->新對象
4. Student.prototype.intr=function(){ … this.sname …}
this-> 將來調用intr()的.前的子對象
第三十二題
第三十三題
第三十四題
判斷是否爲升序數組?
var arr1=[1,2,3,4,5];
var arr2=[2,4,6,4,2];
var result1=arr1.every(function(elem,i,arr){
return i<arr.length-1?elem<arr[i+1]:true;
});
var result2=arr2.every(function(elem,i,arr){
return i<arr.length-1?elem<arr[i+1]:true;
})
console.log(result1,result2);//true false
第三十五題
var arr=[1,2,3,4,5];
arr.forEach(function(elem,i,arr){
// elem*=2//按值傳遞改不了
arr[i]*=2;
})
console.log(arr);
arr=[{x:1},{x:2},{x:3},{x:4},{x:5}];
arr.forEach(function(elem){
elem.x*=2//引用類型,改變的了
})
console.log(arr);
第三十六題
頁面上五個完全相同的按鈕,點哪個按鈕,讓按鈕彈出自己是第幾個?
第三十七題
第三十八題
打散數組
第三十九題
第四十題
數組去重複,有幾種方法,哪種方法最好(考慮數組中包含上萬個元素的情況)
var arr=[];
for(var i=0;i<100000;i++){
arr.push(parseInt(Math.random()*10000));
}
//第一種:傳統,利用對象的屬性名不能重複的特點
function unique1(arr){
var obj={};
for(var n of arr){
obj[n]=1;
}
/*var newArr=[];
for(var key in obj){
newArr.push(parseInt(key));
}*/
//如果字符串內容的數組去重
var newArr=Object.keys(obj);
return newArr;
}
console.time("unique1")
unique1(arr);
console.timeEnd("unique1");
//第二種:利用新ES標準中的新類型Set---最優
//Set類型的對象:是值不允許重複的集合
//向Set類型對象中添加新值,如果Set中沒有這個新值,才能添加進去,如果Set中已經有這個新值了,則不再添加
function unique2(arr){
//將原數組arr放入一個new Set()對象中,利用new Set()不允許重複值的特點,自動去重複
//var set=new Set();
//將set打散後,放入新數組
//var newArr=[...set];
//return newArr
return [...new Set(arr)]
}
console.time("unique2")
unique1(arr);
console.timeEnd("unique2");
第四十一題
第四十二題
第四十三題
將一個類數組對象轉化爲數組的幾種方法
1.slice
語法:slice(begin,end)方法選擇一個從開始到結束(不包括結束)的數組的一部分淺拷貝到一個新的數組對象,方法不會改變原數組。如果是對象則拷貝對象的引用到新數組,如果是基本類型則會拷貝這些值到新數組。
如果省略begin則會從0開始。如果end被省略則會抽取到最後一個元素,如果end數值大於數組長度則會抽取到最後一個元素。
使用Array.prototype.slice.call(arguments)可以將類數組轉化爲數組對象,[].slice.call(arguments)亦可
function list(){
return Array.prototype.slice.call(arguments);
}
var list=list(1,2,3);//[1,2,3]
2.splice
語法:splice(start)
splice(start,deleteCount)
splice(start,deleteCount,item1,item2…)
返回一個包含被刪除元素的數組,start是必須值,其他是可選值,item表示要添加的元素,splice方法會直接對數組進行修改
Array.prototype.splice.call(arguments,0)
3.ES6 Array.from
語法:Array.from(arguments)
4.Array.prototype.concat.apply([],arguments)
第四十四題
arr1.every(function(elem,i,arr){ })內,如果想使用.前的arr1,爲什麼不能用this直接找到.前的對象,而只能用形參中的arr呢?
答:所有回調函數的執行,都是自動執行的,且沒有.前綴。所以,絕大多數回調函數中的this,都指window。
再比如:
var lilei={
sname:“Li Lei”,
intr:function(){ console.log(I'm ${this.sname}
)}
};
lilei.intr(); //this->.前的lilei,所以可以正常輸出lilei.sname
但是:
setTimeout(lilei.intr,3000); //3秒後輸出的卻是undefined
1. 爲什麼lilei.intr不加()?因爲定時器不是立刻調用。而僅僅是給定時器拿走,等3秒後,由定時器自動調用。
2. 爲什麼輸出undefined,而不是lilei.sname?
答: 因爲定時器中的函數,也是回調函數。定時器在拿走函數時,只是單純的拿走了函數的地址,而沒有同時拿走.前的對象lilei。3秒後執行函數時,是直接用函數的地址加()調用的函數。早就不知道函數的所有者是誰了。僅相當於匿名函數自調。匿名函數自調中的this->window,而window.sname當然返回undefined。
第四十五題
匿名函數和回調函數有什麼差別和關係?
答: 回調函數不一定是匿名函數,匿名函數也不一定是回調函數。
只有當回調函數只使用一次時,纔會定義爲匿名函數。如果回調函數可能反覆使用,也要定義爲有名稱的函數。
比如: setTimeout(function(){ … }, 3000)
如果這句話需要反覆使用:
不好的做法: 將setTimeout(連同裏邊的function(){}定義,反覆複製多次!
就應該: 只定義一次函數,然後再定時器中用函數名反覆引用函數:
function task(){ … }
setTimeout(task,3000)
setTimeout(task,2000)
setTimeout(task,1000)
總結: 是否使用匿名函數,取決於將來這個函數被幾處使用。如果只被一處使用,則就用匿名函數。如果多處都需要使用這個函數,則必須起名!
另一方面: 匿名函數也有主動自調的情況,就不屬於回調。
總結: 之所以大多數回調函數都是匿名函數,只是因爲多數回調函數只在一個位置使用一次!所以,沒必要起名。
第四十六題
fromi=fromi||0,按理說,邏輯運算應該返回bool值纔對呀?爲什麼會返回fromi的值或者數字0呢?
答: 邏輯運算有一個特殊的情況,就是,如果邏輯運算前後,都是值時,就不再返回bool值,而是先嚐試轉bool類型做判斷,然後,決定具體返回左邊還是右邊的值
比如: 1||0, 返回1,而不是true
1&&0 返回0, 而不是false
第四十六題
total>=500&&(total*=0.8),可不可以換成三目?
答: 按語義分支結構分爲三種情況:
1. 一件事,滿足條件就執行,不滿足條件就不執行
if實現: if(條件){ 操作 }
比如: if(total>=500){ total*=0.8 }
可簡寫爲: total>=500&&(total*=0.8)
2. 兩件事,根據條件二選一執行
if實現: if(條件){操作1}else{操作2}
比如: if(score>=60){ 輸出及格 }else{ 輸出不及格 }
可用三目簡寫: score>=60?輸出及格:輸出不及格
3. 多件事,根據條件多選一執行
if實現: if(條件1){操作1}
else if(條件2){ 操作2 }
…
else { 默認操作 }
比如: if(score>=90){ 輸出A }
else if(score>=80){ 輸出B }
else if(score>=60){ 輸出C }
else { 輸出D }
可用三目簡寫:
score>=90?輸出A:
score>=80?輸出B:
score>=60?輸出C:
輸出D
所以,選擇何種簡寫,需要根據其語義來選擇。如果只有一件事,選擇做與不做,最好使用短路,而不是三目。三目至少要有兩件事,二選一執行時,才適用。
第四十七題
//1. concat可以將單個元素和數組,都打散後拼接到一個新的數組中
var arr1=[].concat(1,[2,3],4);
//arr1:[1,2,3,4]
console.log(arr1);
//2. 二維數組降維,應該先將數組打散一次後,再將數字和數組混合的參數交給concat,再打散一次拼接。
var arr=[1,[2,3],4,[5,6]]
//錯誤: arr=[].concat(arr); //只能打散一次!
//console.log(arr);
//正確: 先讓apply打散arr,在將打散後的混合數字和數組的多個值交給concat
//arr=Array.prototype.concat.apply(?,arr);
//問題: apply()第一個參數寫誰?
//原理: apply()第一個參數替換concat裏的this,等效於替換concat.前的對象。
//因爲concat函數的主語只能是數組,所以替換concat.前主語的對象只能是一個數組
//arr=Array.prototype.concat.apply([],arr);
//運行時,等效於: [].concat(1,[2,3],4,[5,6])
//3. 簡寫: 從孩子搶方法比從爹搶方法簡單
//第一個[]爲了找到concat方法,而隨便創建的空數組
// |
// __|
// ↓
//arr=[].concat.apply([],arr);
// |
// ______________|
// ↓
//第二個[]是創建一個空數組,代替concat.前的主語,作爲盛放拼接後的結果數組
//4. 用es6代替apply()
arr=[].concat(...arr);
console.log(arr);//[1,2,3,4,5,6]