2019前端面試題之js


 

1、js的數據類型

js的數據類型分為基本類型跟引用類型

基本數據類型(5個):undefined,boolean,number,string,null.基本類型的訪問是按值訪問的,就是說你可以操作保存在變量中的實際的值

基本數據類型的特點:

1.基本類型的值是不可變得:

任何方法都無法改變一個基本類型的值,比如一個字符串:
var name = 'jozo'; name.toUpperCase(); // 輸出 'JOZO' console.log(name); // 輸出 'jozo' 會發現原始的name並未發生改變,而是調用了toUpperCase()方法后返回的是一個新的字符串。 再來看個: var person = 'jozo'; person.age = 22; person.method = function(){//...}; console.log(person.age); // undefined console.log(person.method); // undefined  通過上面代碼可知,我們不能給基本類型添加屬性和方法,再次說明基本類型時不可變得;

2.基本類型的比較是值的比較:

只有在它們的值相等的時候它們才相等。
但你可能會這樣:

var a = 1;
var b = true;
console.log(a == b);//true

它們不是相等嗎?其實這是類型轉換和 == 運算符的知識了,也就是說在用==比較兩個不同類型的變量時會進行一些類型轉換。像上面的比較先會把true
轉換為數字1再和數字1進行比較,結果就是true了。 這是當比較的兩個值的類型不同的時候==運算符會進行類型轉換,但是當兩個值的類型相同的時候,
即使是==也相當於是===。

var a = 'jozo';
var b = 'jozo';
console.log(a === b);//true

3.基本類型的變量是存放在棧區的(棧區指內存里的棧內存)

假如有以下幾個基本類型的變量:

var name = 'jozo';
var city = 'guangzhou';
var age = 22;
那么它的存儲結構如下圖:

 

2.引用類型

引用類型會比較好玩有趣一些。

javascript中除了上面的基本類型(number,string,boolean,null,undefined)之外就是引用類型了,也可以說是就是對象了。對象是屬性和方法的集合。
也就是說引用類型可以擁有屬性和方法,屬性又可以包含基本類型和引用類型。來看看引用類型的一些特性:

1.引用類型的值是可變的

我們可為為引用類型添加屬性和方法,也可以刪除其屬性和方法,如:

var person = {};//創建個控對象 --引用類型
person.name = 'jozo';
person.age = 22;
person.sayName = function(){console.log(person.name);}
person.sayName();// 'jozo'

delete person.name; //刪除person對象的name屬性
person.sayName(); // undefined

上面代碼說明引用類型可以擁有屬性和方法,並且是可以動態改變的。

2.引用類型的值是同時保存在棧內存和堆內存中的對象

javascript和其他語言不同,其不允許直接訪問內存中的位置,也就是說不能直接操作對象的內存空間,那我們操作啥呢? 實際上,是操作對象的引用,
所以引用類型的值是按引用訪問的。
准確地說,引用類型的存儲需要內存的棧區和堆區(堆區是指內存里的堆內存)共同完成,棧區內存保存變量標識符和指向堆內存中該對象的指針,
也可以說是該對象在堆內存的地址。
假如有以下幾個對象
var person1 = {name:'jozo'};
var person2 = {name:'xiaom'};
var person3 = {name:'xiaoq'};
則這三個對象的在內存中保存的情況如下圖:

3.引用類型的比較是引用的比較

var person1 = '{}';
var person2 = '{}';
console.log(person1 == person2); // true

上面講基本類型的比較的時候提到了當兩個比較值的類型相同的時候,相當於是用 === ,所以輸出是true了。再看看:

var person1 = {};
var person2 = {};
console.log(person1 == person2); // false

可能你已經看出破綻了,上面比較的是兩個字符串,而下面比較的是兩個對象,為什么長的一模一樣的對象就不相等了呢?

別忘了,引用類型時按引用訪問的,換句話說就是比較兩個對象的堆內存中的地址是否相同,那很明顯,person1和person2在堆內存中地址是不同的:

所以這兩個是完全不同的對象,所以返回false;

3.簡單賦值

在從一個變量向另一個變量賦值基本類型時,會在該變量上創建一個新值,然后再把該值復制到為新變量分配的位置上:

var a = 10;
var b = a;

a ++ ;
console.log(a); // 11
console.log(b); // 10

此時,a中保存的值為 10 ,當使用 a 來初始化 b 時,b 中保存的值也為10,但b中的10與a中的是完全獨立的,該值只是a中的值的一個副本,此后,
這兩個變量可以參加任何操作而相互不受影響。

 

也就是說基本類型在賦值操作后,兩個變量是相互不受影響的。

4.對象引用

當從一個變量向另一個變量賦值引用類型的值時,同樣也會將存儲在變量中的對象的值復制一份放到為新變量分配的空間中。前面講引用類型的時候提到,
保存在變量中的是對象在堆內存中的地址,所以,與簡單賦值不同,這個值的副本實際上是一個指針,而這個指針指向存儲在堆內存的一個對象。那么賦值操作后,
兩個變量都保存了同一個對象地址,則這兩個變量指向了同一個對象。因此,改變其中任何一個變量,都會相互影響:

var a = {}; // a保存了一個空對象的實例
var b = a; // a和b都指向了這個空對象

a.name = 'jozo';
console.log(a.name); // 'jozo'
console.log(b.name); // 'jozo'

b.age = 22;
console.log(b.age);// 22
console.log(a.age);// 22

console.log(a == b);// true

它們的關系如下圖:

因此,引用類型的賦值其實是對象保存在棧區地址指針的賦值,因此兩個變量指向同一個對象,任何的操作都會相互影響。

2、jquery的鏈式調用原理

原理是因為jquery是用了原型鏈然后是通過return this;返回這個對象,所以可以連着調用即連着寫入,但是不能連着讀取。

3、判斷數據類型

使用typeof判斷數據類型,但是對於obj沒辦法進行判斷(obj包括array、obj、function)。

所以我們可以通過使用constructor來判斷,對象的constructor屬性用於返回創建該對象的函數,也就是我們常說的構造函數。 

// 字符串:String()
var str = "張三";
document.writeln(str.constructor); // function String() { [native code] }
document.writeln(str.constructor === String); // true

// 數組:Array()
var arr = [1, 2, 3];
document.writeln(arr.constructor); // function Array() { [native code] }
document.writeln(arr.constructor === Array); // true

// 數字:Number()
var num = 5;
document.writeln(num.constructor); // function Number() { [native code] }
document.writeln(num.constructor === Number); // true

// 自定義對象:Person()
function Person(){
    this.name = "CodePlayer";
}
var p = new Person();
document.writeln(p.constructor); // function Person(){ this.name = "CodePlayer"; }
document.writeln(p.constructor === Person); // true

// JSON對象:Object()
var o = { "name" : "張三"};
document.writeln(o.constructor); // function Object() { [native code] }
document.writeln(o.constructor === Object); // true

// 自定義函數:Function()
function foo(){
    alert("CodePlayer");
}
document.writeln(foo.constructor); // function Function() { [native code] }
document.writeln(foo.constructor === Function); // true

// 函數的原型:bar()
function bar(){
    alert("CodePlayer");
}
document.writeln(bar.prototype.constructor); // function bar(){ alert("CodePlayer"); }
document.writeln(bar.prototype.constructor === bar); // true

4、js繼承之原型鏈繼承

每創建一個函數,該函數就會自動帶有一個 prototype 屬性。該屬性是個指針,指向了一個對象,我們稱之為 原型對象。什么是指針?指針就好比學生的學號,原型對象則是那個學生。我們通過學號找到唯一的那個學生。假設突然,指針設置 null, 學號重置空了,不要慌,對象還存在,學生也沒消失。只是不好找了。

原型對象上默認有一個屬性 constructor,該屬性也是一個指針,指向其相關聯的構造函數。

通過調用構造函數產生的實例,都有一個內部屬性,指向了原型對象。所以實例能夠訪問原型對象上的所有屬性和方法。  

所以三者的關系是,每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。通俗點說就是,實例通過內部指針可以訪問到原型對象,原型對象通過constructor指針,又可以找到構造函數。

function Dog (name) {
    this.name = name;
    this.type = 'Dog'; 
}
Dog.prototype.speak = function () {
  alert('wang');
}
var doggie = new Dog('jiwawa');
doggie.speak();  //wang 

以上代碼定義了一個構造函數 Dog(),  Dog.prototype 指向的原型對象,其自帶的屬性construtor又指回了 Dog,即  Dog.prototype.constructor == Dog. 實例doggie由於其內部指針指向了該原型對象,所以可以訪問到 speak方法。

Dog.prototype 只是一個指針,指向的是原型對象,但是這個原型對象並不特別,它也只是一個普通對象。假設說,這時候,我們讓 Dog.protptype 不再指向最初的原型對象,而是另一個類 (Animal)的實例,情況會怎樣呢?

 

 

二、原型鏈

 

  前面我們說到,所有的實例有一個內部指針,指向它的原型對象,並且可以訪問原型對象上的所有屬性和方法。doggie實例指向了Dog的原型對象,可以訪問Dog原型對象上的所有屬性和方法;如果Dog原型對象變成了某一個類的實例 aaa,這個實例又會指向一個新的原型對象 AAA,那么 doggie 此時就能訪問 aaa 的實例屬性和 AA A原型對象上的所有屬性和方法了。同理,新的原型對象AAA碰巧又是另外一個對象的實例bbb,這個實例bbb又會指向新的原型對象 BBB,那么doggie此時就能訪問 bbb 的實例屬性和 BBB 原型對象上的所有屬性和方法了。

 

  這就是JS通過原型鏈實現繼承的方法了。看下面一個例子:

//定義一個 Animal 構造函數,作為 Dog 的父類
function Animal () {
    this.superType = 'Animal';
}

Animal.prototype.superSpeak = function () {
    alert(this.superType);
}

function Dog (name) {
    this.name = name;
    this.type = 'Dog';  
}
//改變Dog的prototype指針,指向一個 Animal 實例
Dog.prototype = new Animal();
//上面那行就相當於這么寫
//var animal = new Animal();
//Dog.prototype = animal;

Dog.prototype.speak = function () {
  alert(this.type);
}
var doggie = new Dog('jiwawa');
doggie.superSpeak();  //Animal 

解釋一下。以上代碼,首先定義了一個 Animal 構造函數,通過new Animal()得到實例,會包含一個實例屬性 superType 和一個原型屬性 superSpeak。另外又定義了一個Dog構造函數。然后情況發生變化,代碼中加粗那一行,將Dog的原型對象覆蓋成了 animal 實例。當 doggie 去訪問superSpeak屬性時,js會先在doggie的實例屬性中查找,發現找不到,然后,js就會去doggie 的原型對象上去找,doggie的原型對象已經被我們改成了一個animal實例,那就是去animal實例上去找。先找animal的實例屬性,發現還是沒有 superSpeack, 最后去 animal 的原型對象上去找,誒,這才找到。

這就說明,我們可以通過原型鏈的方式,實現 Dog 繼承 Animal 的所有屬性和方法。

  總結來說:就是當重寫了Dog.prototype指向的原型對象后,實例的內部指針也發生了改變,指向了新的原型對象,然后就能實現類與類之間的繼承了。(但是如果在重寫原型對象之前,產生的實例,其內部指針指向的還是最初的原型對象。這個我下次再發篇文章講。js 繼承之借用構造函數繼承

加深  http://www.cnblogs.com/sarahwang/p/6879161.html

5、JavaScript設計模式

6、怎么減少dom操作

7、var let const

var可以變量提升而另外2個不能,另外兩個不能重復聲明,有塊作用域

8、JavaScript 事件委托詳解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM