JS面試題(一)

1.document load 和 document ready 的區(qū)別

頁面加載完成有兩種事件

1.load是當(dāng)頁面所有資源全部加載完成后(包括DOM文檔樹,css文件,js文件,圖片資源等),執(zhí)行一個函數(shù)
問題:如果圖片資源較多,加載時間較長,onload后等待執(zhí)行的函數(shù)需要等待較長時間,所以一些效果可能受到影響

2.$(document).ready()是當(dāng)DOM文檔樹加載完成后執(zhí)行一個函數(shù) (不包含圖片,css等)所以會比load較快執(zhí)行
在原生的jS中不包括ready()這個方法,只有l(wèi)oad方法就是onload事件

2. JavaScript 中如何檢測一個變量是一個 String 類型?

三種方法(typeof、constructor、Object.prototype.toString.call())解析:①typeof

typeof('123') === "string" // true

typeof '123' === "string" // true

②constructor

'123'.constructor === String // true

③Object.prototype.toString.call()

Object.prototype.toString.call('123') === '[object String]' // true

3.請用 js 去除字符串空格?

replace 正則匹配方法、str.trim()方法、JQ 方法:.trim(str)方法解析:方法一:replace 正則匹配方法去除字符串內(nèi)所有的空格:str = str.replace(/\s*/g,"");去除字符串內(nèi)兩頭的空格:str = str.replace(/^\s*|\s*/g,"");去除字符串內(nèi)左側(cè)的空格:str = str.replace(/^\s/,"");去除字符串內(nèi)右側(cè)的空格:str = str.replace(/(\s$)/g,"");示例:var str = " 6 6 ";
var str_1 = str.replace(/\s*/g, "");
console.log(str_1); //66

var str = " 6 6 ";
var str_1 = str.replace(/^\s|\s$/g, "");
console.log(str_1); //6 6//輸出左右側(cè)均無空格

var str = " 6 6 ";
var str_1 = str.replace(/^\s*/, "");
console.log(str_1); //6 6 //輸出右側(cè)有空格左側(cè)無空格

var str = " 6 6 ";
var str_1 = str.replace(/(\s*)/g, ""); console.log(str_1); // 6 6//輸出左側(cè)有空格右側(cè)無空格方法二:str.trim()方法trim()方法是用來刪除字符串兩端的空白字符并返回,trim 方法并不影響原來的字符串本身,它返回的是一個新的字符串。缺陷:只能去除字符串兩端的空格,不能去除中間的空格示例:var str = " 6 6 "; var str_1 = str.trim(); console.log(str_1); //6 6//輸出左右側(cè)均無空格方法三:JQ 方法:.trim(str)方法.trim() 函數(shù)用于去除字符串兩端的空白字符。注意:.trim()函數(shù)會移除字符串開始和末尾處的所有換行符,空格(包括連續(xù)的空格)和制表符。如果這些空白字符在字符串中間時,它們將被保留,不會被移除。示例:var str = " 6 6 ";
var str_1 = $.trim(str);
console.log(str_1); //6 6//輸出左右側(cè)均無空格

4.js 是一門怎樣的語言,它有什么特點

1.腳本語言。JavaScript 是一種解釋型的腳本語言,C、C++等語言先編譯后執(zhí)行,而 JavaScript 是在程序的運行過程中逐行進行解釋。2.基于對象。JavaScript 是一種基于對象的腳本語言,它不僅可以創(chuàng)建對象,也能使用現(xiàn)有的對象。3.簡單。JavaScript 語言中采用的是弱類型的變量類型,對使用的數(shù)據(jù)類型未做出嚴(yán)格的要求,是基于 Java 基本語句和控制的腳本語言,其設(shè)計簡單緊湊。4.動態(tài)性。JavaScript 是一種采用事件驅(qū)動的腳本語言,它不需要經(jīng)過 Web 服務(wù)器就可以對用戶的輸入做出響應(yīng)。5.跨平臺性。JavaScript 腳本語言不依賴于操作系統(tǒng),僅需要瀏覽器的支持。

5.== 和 === 的不同

==是抽象相等運算符,而===是嚴(yán)格相等運算符。==運算符是在進行必要的類型轉(zhuǎn)換后,再比較。===運算符不會進行類型轉(zhuǎn)換,所以如果兩個值不是相同的類型,會直接返回false。使用==時,可能發(fā)生一些特別的事情,例如:1 == "1"; // true
1 == [1]; // true
1 == true; // true
0 == ""; // true
0 == "0"; // true
0 == false; // true如果你對==和===的概念不是特別了解,建議大多數(shù)情況下使用===

6.怎樣添加、移除、移動、復(fù)制、創(chuàng)建和查找節(jié)點?

1)創(chuàng)建新節(jié)點
createDocumentFragment() //創(chuàng)建一個 DOM 片段
createElement() //創(chuàng)建一個具體的元素
createTextNode() //創(chuàng)建一個文本節(jié)點
2)添加、移除、替換、插入
appendChild() //添加
removeChild() //移除
replaceChild() //替換
insertBefore() //插入
3)查找
getElementsByTagName() //通過標(biāo)簽名稱
getElementsByName() //通過元素的 Name 屬性的值
getElementById() //通過元素 Id,唯一性

7.事件委托是什么

利用事件冒泡的原理,讓自己的所觸發(fā)的事件,讓他的父元素代替執(zhí)行!解析:1、那什么樣的事件可以用事件委托,什么樣的事件不可以用呢?
適合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
值得注意的是,mouseover 和 mouseout 雖然也有事件冒泡,但是處理它們的時候需要特別的注意,因為需要經(jīng)常計算它們的位置,處理起來不太容易。
不適合的就有很多了,舉個例子,mousemove,每次都要計算它的位置,非常不好把控,在不如說 focus,blur 之類的,本身就沒用冒泡的特性,自然就不用事件委托了。
2、為什么要用事件委托
1.提高性能
(```)
?ul?
?li?蘋果?/li?
?li?香蕉?/li?
?li?鳳梨?/li?
?/ul?

// good
document.querySelector('ul').onclick = (event) =? {
let target = event.target
if (target.nodeName === 'LI') {
console.log(target.innerHTML)
}
}

// bad
document.querySelectorAll('li').forEach((e) =? {
e.onclick = function() {
console.log(this.innerHTML)
}
})

2.新添加的元素還會有之前的事件。
3、事件冒泡與事件委托的對比
事件冒泡:box 內(nèi)部無論是什么元素,點擊后都會觸發(fā) box 的點擊事件
事件委托:可以對 box 內(nèi)部的元素進行篩選
4、事件委托怎么取索引?
?ul id="ul"?
?li?aaaaaaaa?/li?
?li?事件委托了 點擊當(dāng)前,如何獲取 這個點擊的下標(biāo)?/li?
?li?cccccccc?/li?
?/ul?
?script? window.onload = function () { var oUl = document.getElementById("ul"); var aLi = oUl.getElementsByTagName("li"); oUl.onclick = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == "li") { var that = target; var index; for (var i = 0; i ? aLi.length; i++) if (aLi[i] === target) index = i; if (index ?= 0) alert('我的下標(biāo)是第' + index + '個'); target.style.background = "red"; } } } ?/script?

8.require 與 import 的區(qū)別

兩者的加載方式不同、規(guī)范不同第一、兩者的加載方式不同,require 是在運行時加載,而 import 是在編譯時加載require('./a')(); // a 模塊是一個函數(shù),立即執(zhí)行 a 模塊函數(shù)var data = require('./a').data; // a 模塊導(dǎo)出的是一個對象var a = require('./a')[0]; // a 模塊導(dǎo)出的是一個數(shù)組 ======? 哪都行import $ from 'jquery';import * as _ from '_';import {a,b,c} from './a';import {default as alias, a as a_a, b, c} from './a'; ======?用在開頭第二、規(guī)范不同,require 是 CommonJS/AMD 規(guī)范,import 是 ESMAScript6+規(guī)范第三、require 特點:社區(qū)方案,提供了服務(wù)器/瀏覽器的模塊加載方案。非語言層面的標(biāo)準(zhǔn)。只能在運行時確定模塊的依賴關(guān)系及輸入/輸出的變量,無法進行靜態(tài)優(yōu)化。import 特點:語言規(guī)格層面支持模塊功能。支持編譯時靜態(tài)分析,便于 JS 引入宏和類型檢驗。動態(tài)綁定。

9.javascript 對象的幾種創(chuàng)建方式

第一種:Object 構(gòu)造函數(shù)創(chuàng)建var Person = new Object()

Person.name = "Nike";
Person.age = 29;這行代碼創(chuàng)建了 Object 引用類型的一個新實例,然后把實例保存在變量 Person 中。第二種:使用對象字面量表示法var Person = {}; //相當(dāng)于 var Person = new Object();
var Person = {
name: 'Nike';
age: 29;
}對象字面量是對象定義的一種簡寫形式,目的在于簡化創(chuàng)建包含大量屬性的對象的過程。也就是說,第一種和第二種方式創(chuàng)建對象的方法其實都是一樣的,只是寫法上的區(qū)別不同在介紹第三種的創(chuàng)建方法之前,我們應(yīng)該要明白為什么還要用別的方法來創(chuàng)建對象,也就是第一種,第二種方法的缺點所在:它們都是用了同一個接口創(chuàng)建很多對象,會產(chǎn)生大量的重復(fù)代碼,就是如果你有 100 個對象,那你要輸入 100 次很多相同的代碼。那我們有什么方法來避免過多的重復(fù)代碼呢,就是把創(chuàng)建對象的過程封裝在函數(shù)體內(nèi),通過函數(shù)的調(diào)用直接生成對象。第三種:使用工廠模式創(chuàng)建對象function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {

this.name);
};
return o;
}
var person1 = createPerson("Nike", 29, "teacher");
var person2 = createPerson("Arvin", 20, "student");在使用工廠模式創(chuàng)建對象的時候,我們都可以注意到,在 createPerson 函數(shù)中,返回的是一個對象。那么我們就無法判斷返回的對象究竟是一個什么樣的類型。于是就出現(xiàn)了第四種創(chuàng)建對象的模式。第四種:使用構(gòu)造函數(shù)創(chuàng)建對象function Person(name, age, job) {

this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
this.name);
};
}
var person1 = new Person("Nike", 29, "teacher");
var person2 = new Person("Arvin", 20, "student");對比工廠模式,我們可以發(fā)現(xiàn)以下區(qū)別:1.沒有顯示地創(chuàng)建對象2.直接將屬性和方法賦給了 this 對象3.沒有 return 語句4.終于可以識別的對象的類型。對于檢測對象類型,我們應(yīng)該使用 instanceof 操作符,我們來進行自主檢測:alert(person1 instanceof Object); //ture

alert(person1 instanceof Person); //ture

alert(person2 instanceof Object); //ture

alert(person2 instanceof Object); //ture同時我們也應(yīng)該明白,按照慣例,構(gòu)造函數(shù)始終要應(yīng)該以一個大寫字母開頭,而非構(gòu)造函數(shù)則應(yīng)該以一個小寫字母開頭。那么構(gòu)造函數(shù)確實挺好用的,但是它也有它的缺點:就是每個方法都要在每個實例上重新創(chuàng)建一遍,方法指的就是我們在對象里面定義的函數(shù)。如果方法的數(shù)量很多,就會占用很多不必要的內(nèi)存。于是出現(xiàn)了第五種創(chuàng)建對象的方法第五種:原型創(chuàng)建對象模式function Person() {

Person.prototype.name = "Nike";
Person.prototype.age = 20;
Person.prototype.jbo = "teacher";
Person.prototype.sayName = function() {

this.name);
};
var person1 = new Person();
person1.sayName();使用原型創(chuàng)建對象的方式,可以讓所有對象實例共享它所包含的屬性和方法。如果是使用原型創(chuàng)建對象模式,請看下面代碼:function Person() {}

Person.prototype.name = "Nike";
Person.prototype.age = 20;
Person.prototype.jbo = "teacher";
Person.prototype.sayName = function() {

this.name);
};
var person1 = new Person();
var person2 = new Person();

person1.name = "Greg";

person1.name); //'Greg' --來自實例

person2.name); //'Nike' --來自原型當(dāng)為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性。這時候我們就可以使用構(gòu)造函數(shù)模式與原型模式結(jié)合的方式,構(gòu)造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性第六種:組合使用構(gòu)造函數(shù)模式和原型模式function Person(name, age, job) {

this.name = name;
this.age = age;
this.job = job;
}
Person.prototype = {
constructor: Person,
sayName: function() {

this.name);
};
}
var person1 = new Person('Nike', 20, 'teacher');

10.JavaScript 繼承的方式和優(yōu)缺點

六種方式

一、原型鏈繼承

缺點:
1.引用類型的屬性被所有實例共享
2.在創(chuàng)建 Child 的實例時,不能向 Parent 傳參

二、借用構(gòu)造函數(shù)(經(jīng)典繼承)

優(yōu)點:
1.避免了引用類型的屬性被所有實例共享
2.可以在 Child 中向 Parent 傳參

缺點:
1.方法都在構(gòu)造函數(shù)中定義,每次創(chuàng)建實例都會創(chuàng)建一遍方法。

三、組合繼承

優(yōu)點:
1.融合原型鏈繼承和構(gòu)造函數(shù)的優(yōu)點,是 JavaScript 中最常用的繼承模式。

四、原型式繼承

缺點:
1.包含引用類型的屬性值始終都會共享相應(yīng)的值,這點跟原型鏈繼承一樣。

五、寄生式繼承

缺點:
1.跟借用構(gòu)造函數(shù)模式一樣,每次創(chuàng)建對象都會創(chuàng)建一遍方法。

六、寄生組合式繼承

優(yōu)點:
1.這種方式的高效率體現(xiàn)它只調(diào)用了一次 Parent 構(gòu)造函數(shù),并且因此避免了在 Parent.prototype 上面創(chuàng)建不必要的、多余的屬性。
2.與此同時,原型鏈還能保持不變;
3.因此,還能夠正常使用 instanceof 和 isPrototypeOf。
開發(fā)人員普遍認(rèn)為寄生組合式繼承是引用類型最理想的繼承范式

11.復(fù)雜數(shù)據(jù)類型如何轉(zhuǎn)變?yōu)樽址?/h5>

首先,會調(diào)用 valueOf 方法,如果方法的返回值是一個基本數(shù)據(jù)類型,就返回這個值,
如果調(diào)用 valueOf 方法之后的返回值仍舊是一個復(fù)雜數(shù)據(jù)類型,就會調(diào)用該對象的 toString 方法,
如果 toString 方法調(diào)用之后的返回值是一個基本數(shù)據(jù)類型,就返回這個值,
如果 toString 方法調(diào)用之后的返回值是一個復(fù)雜數(shù)據(jù)類型,就報一個錯誤。
解析:1;
var obj = {
valueOf: function() {
return 1;
}
};
console.log(obj + ""); //'1'
2;
var obj = {
valueOf: function() {
return [1, 2];
}
};
console.log(obj + ""); //'[object Object]';
3;
var obj = {
valueOf: function() {
return [1, 2];
},
toString: function() {
return 1;
}
};
console.log(obj + ""); //'1';
4;
var obj = {
valueOf: function() {
return [1, 2];
},
toString: function() {
return [1, 2, 3];
}
};
console.log(obj + ""); // 報錯 Uncaught TypeError: Cannot convert object to primitive value拓展:var arr = [new Object(), new Date(), new RegExp(), new String(), new Number(), new Boolean(), new Function(), new Array(), Math] console.log(arr.length) // 9
for (var i = 0; i ? arr.length; i++) {
arr[i].valueOf = function() {
return [1, 2, 3]
}
arr[i].toString = function() {
return 'toString'
}
console.log(arr[i] + '')
}1、若 return [1,2,3]處為 return "valueof",得到的返回值是 valueof toString 7valueof
說明:其他八種復(fù)雜數(shù)據(jù)類型是先調(diào)用 valueOf 方法,時間對象是先調(diào)用 toString 方法2、改成 return [1,2,3],得到的返回值是 9toString
說明:執(zhí)行 valueof 后都來執(zhí)行 toString

12.javascript 的 typeof 返回哪些數(shù)據(jù)類型

7 種分別為 string、boolean、number、Object、Function、undefined、symbol(ES6)、

13. 在 css/js 代碼上線之后開發(fā)人員經(jīng)常會優(yōu)化性能,從用戶刷新網(wǎng)頁開始,一次 js 請求一般情況下有哪些地方會有緩存處理?

dns 緩存,cdn 緩存,瀏覽器緩存,服務(wù)器緩存。

14.你對閉包的理解?優(yōu)缺點?

概念:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。三大特性:
函數(shù)嵌套函數(shù)。
函數(shù)內(nèi)部可以引用外部的參數(shù)和變量。
參數(shù)和變量不會被垃圾回收機制回收。
優(yōu)點:
希望一個變量長期存儲在內(nèi)存中。
避免全局變量的污染。
私有成員的存在。
缺點:
常駐內(nèi)存,增加內(nèi)存使用量。
使用不當(dāng)會很容易造成內(nèi)存泄露。
示例:function outer() {
var name = "jack";
function inner() {
console.log(name);
}
return inner;
}
outer()(); // jackfunction sayHi(name) {
return () =? {
console.log(Hi! ${name});
};
}
const test = sayHi("xiaoming");
test(); // Hi! xiaoming雖然 sayHi 函數(shù)已經(jīng)執(zhí)行完畢,但是其活動對象也不會被銷毀,因為 test 函數(shù)仍然引用著 sayHi 函數(shù)中的變量 name,這就是閉包。
但也因為閉包引用著另一個函數(shù)的變量,導(dǎo)致另一個函數(shù)已經(jīng)不使用了也無法銷毀,所以閉包使用過多,會占用較多的內(nèi)存,這也是一個副作用。解析:由于在 ECMA2015 中,只有函數(shù)才能分割作用域,函數(shù)內(nèi)部可以訪問當(dāng)前作用域的變量,但是外部無法訪問函數(shù)內(nèi)部的變量,所以閉包可以理解成“定義在一個函數(shù)內(nèi)部的函數(shù),外部可以通過內(nèi)部返回的函數(shù)訪問內(nèi)部函數(shù)的變量“。在本質(zhì)上,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁。

15.如何判斷 NaN

isNaN()方法解析:isNaN(NaN) // true

16.for in 和 for of

1、for in
1.一般用于遍歷對象的可枚舉屬性。以及對象從構(gòu)造函數(shù)原型中繼承的屬性。對于每個不同的屬性,語句都會被執(zhí)行。
2.不建議使用 for in 遍歷數(shù)組,因為輸出的順序是不固定的。
3.如果迭代的對象的變量值是 null 或者 undefined, for in 不執(zhí)行循環(huán)體,建議在使用 for in 循環(huán)之前,先檢查該對象的值是不是 null 或者 undefined
2、for of
1.for…of 語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創(chuàng)建一個迭代循環(huán),調(diào)用自定義迭代鉤子,并為每個不同屬性的值執(zhí)行語句
解析:var s = {
a: 1,
b: 2,
c: 3
};
var s1 = Object.create(s);
for (var prop in s1) {
console.log(prop); //a b c
console.log(s1[prop]); //1 2 3
}
for (let prop of s1) {
console.log(prop); //報錯如下 Uncaught TypeError: s1 is not iterable
}
for (let prop of Object.keys(s1)) {
console.log(prop); // a b c
console.log(s1[prop]); //1 2 3
}

17. for in、Object.keys 和 Object.getOwnPropertyNames 對屬性遍歷有什么區(qū)別?

for in 會遍歷自身及原型鏈上的可枚舉屬性
Object.keys 會將對象自身的可枚舉屬性的 key 輸出
會將自身所有的屬性的 key 輸出
解析:ECMAScript 將對象的屬性分為兩種:數(shù)據(jù)屬性和訪問器屬性。var parent = Object.create(Object.prototype, {
a: {
value: 123,
writable: true,
enumerable: true,
configurable: true
}
});
// parent繼承自O(shè)bject.prototype,有一個可枚舉的屬性a(enumerable:true)。

var child = Object.create(parent, {
b: {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
c: {
value: 3,
writable: true,
enumerable: false,
configurable: true
}
});
//child 繼承自 parent ,b可枚舉,c不可枚舉for infor (var key in child) {
console.log(key);
}
// b
// a
// for in 會遍歷自身及原型鏈上的可枚舉屬性如果只想輸出自身的可枚舉屬性,可使用 hasOwnProperty 進行判斷(數(shù)組與對象都可以,此處用數(shù)組做例子)let arr = [1, 2, 3];
Array.prototype.xxx = 1231235;
for (let i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(arr[i]);
}
}
// 1
// 2
// 3Object.keysconsole.log(Object.keys(child));
// ["b"]
// Object.keys 會將對象自身的可枚舉屬性的key輸出Object.getOwnPropertyNamesconsole.log(Object.getOwnPropertyNames(child));
// ["b","c"]
// 會將自身所有的屬性的key輸出

18.iframe 跨域通信和不跨域通信

不跨域通信主頁面?!DOCTYPE html?
?html?
?head?
?meta charset="utf-8" /?
?title??/title?
?/head?
?body?
?iframe
name="myIframe"
id="iframe"
class=""
src="flexible.html"
width="500px"
height="500px"
?
?/iframe?
?/body?
?script type="text/javascript" charset="utf-8"?
function fullscreen() {
alert(1111);
}
?/script?
?/html?子頁面 flexible.html?!DOCTYPE html?
?html?
?head?
?meta charset="utf-8" /?
?title??/title?
?/head?
?body?
我是子頁面
?/body?
?script type="text/javascript" charset="utf-8"?
// window.parent.fullScreens()
function showalert() {
alert(222);
}
?/script?
?/html?1、主頁面要是想要調(diào)取子頁面的 showalert 方法myIframe.window.showalert();2、子頁面要掉主頁面的 fullscreen 方法window.parent.fullScreens();3、js 在 iframe 子頁面獲取父頁面元素:window.parent.document.getElementById("元素id");4、js 在父頁面獲取 iframe 子頁面元素代碼如下:window.frames["iframe_ID"].document.getElementById("元素id");跨域通信使用postMessage(官方用法)子頁面window.parent.postMessage("hello", "http://127.0.0.1:8089");父頁面接收window.addEventListener("message", function(event) {
alert(123);
});

19.H5 與 Native 如何交互

jsBridge

20.如何判斷一個對象是否為數(shù)組

第一種方法:使用 instanceof 操作符。第二種方法:使用 ECMAScript 5 新增的 Array.isArray()方法。第三種方法:使用使用 Object.prototype 上的原生 toString()方法判斷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容