1、typeof和 instanceof差別(必會)
在 javascript 中,判斷一個變量的類型可以用 typeof
- 1、數字類型、typeof 傳回的值是 number。比如說:typeof(1),傳回值是 number
- 2、字元串類型,typeof 傳回的值是 string。比如 typeof(“123”傳回值是 string
- 3、布爾類型,typeof 傳回的值是 boolean。比如 typeof(true)傳回值是 boolean
- 4、對象、數組、null 傳回的值是 object。比如 typeof(window),typeof(document),typeof(null)傳回的值都是 object
- 5、函數類型,傳回的值是 function。比如:typeof(eval),typeof(Date)傳回的值都是 function。
- 6、不存在的變量、函數或者 undefined,将傳回 undefined。比如:typeof(abc)、typeof(undefined)都傳回 undefined
在 javascript 中,instanceof 用于判斷某個對象是否被另一個函數構造使用 typeof 運算符時采用引用類型存儲值會出現一個問題,無論引用的是什麼類型的對象,它都傳回”object”。ECMAScript 引入了另一個 Java 運算符 instanceof 來解決這個問題。
Instanceof 運算符與 typeof 運算符相似,用于識别正在處理的對象的類型。與 typeof 方法不同的是,instanceof 方法要求開發者明确地确認對象為某特定類型
2、js使用 typeof能得到的哪些類型?(必會)
typeof 隻能區分值類型
typeof undefined // undefined
typeof null // object
typeof console.log // function
typeof NaN // number
3、解釋一下什麼是回調函數,并提供一個簡單的例子?(必會)
軟體子產品之間總是存在着一定的接口,從調用方式上,可以把他們分為三類:同步調用 、回調 和 異步調用
同步調用 是一種阻塞式調用,調用方要等待對方執行完畢才 傳回,它是一種單向調用;
回調 是一種雙向調用模式,也就是說,被調用方在接口被調用時也會調用對方的接口;
異步調用 是一種類似消息或事件的機制,不過它的 調用方向剛好相反,接口的服務在收到某種訊息或發生某種事件時,會主動通知客戶方(即調用客戶方的接口)。回調和異步調用的關系非常緊密,通常我們使用回 調來實作異步消息的注冊,通過異步調用來實作消息的通知。同步調用是三者當中最簡單的,而回調又常常是異步調用的基礎,是以,下面我們着重讨論回調機制在不同軟體架構中的實作
回調函數 就是一個通過函數指針調用的函數。如果你把函數的指針(位址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實作方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應
案例:
#include<stdio.h>
//callbackTest.c
//1.定義函數 onHeight(回調函數)
//@onHeight 函數名
//@height 參數
//@contex 上下文
void onHeight(double height, void *contex)
{
printf("current height is %lf", height);
}
//2.定義 onHeight 函數的原型
//@CallbackFun 指向函數的指針類型
//@height 回調參數,當有多個參數時,可以定義一個結構體
//@contex 回調上下文,在 C 中一般傳入 nullptr,在 C++中可傳入對象指針
typedef void (*CallbackFun)(double height, void *contex);
//定義全局指針變量
CallbackFun m_pCallback;
//定義注冊回調函數
void registHeightCallback(CallbackFun callback, void *contex)
{
m_pCallback = callback;
}
//定義調用函數
void printHeightFun(double height)
{
m_pCallback(height, NULL);
}
//main 函數
int main()
{
//注冊回調函數 onHeight
registHeightCallback(onHeight, NULL);
//列印 height
double h = 99;
printHeightFun(99);
}
4、什麼是閉包?(必會)
“閉包就是能夠讀取其他函數内部變量的函數。例如在 javascript 中,隻有函數内部的子函數才能讀取局部變量,是以閉包可以了解成“定義在一個函數内部的函數“。在本質上,閉包是将函數内部和函數外部連接配接起來的橋梁。”
舉例:建立閉包最常見方式,就是在一個函數内部建立另一個函數。下面例子中的 closure 就是一個閉包
function func(){
var a =1 ,b = 2;
funciton closure(){
return a + b;
}
return closure;
}
5、什麼是記憶體洩漏(必會)
記憶體洩漏指任何對象在您不再擁有或需要它之後仍然存在
6、哪些操作會造成記憶體洩漏?(必會)
- 1、垃圾回收器定期掃描對象,并計算引用了每個對象的其他對象的數量。如果一個對象的引用數量為 0(沒有其他對象引用過該對象),或對該對象的惟一引用是循環的,那麼該對象的記憶體即可回收
- 2、setTimeout 的第一個參數使用字元串而非函數的話,會引發記憶體洩漏
- 3、閉包、控制台日志、循環(在兩個對象彼此引用且彼此保留時,就會産生一個循環)
7、JS記憶體洩漏的解決方式(必會)
7.1 global variables:對未聲明的變量的引用在全局對象内建立一個新變量。在浏覽器中,全局對象就是 window。
function foo(arg) {
bar = 'some text'; // 等同于 window.bar = 'some text';
}
- 解決 :
- 建立意外的全局變量
function foo() {
this.var1 = 'potential accident'
}
- 可以在 JavaScript 檔案開頭添加 “use strict”,使用嚴格模式。這樣在嚴格模式下解析 JavaScript 可以防止意外的全局變量
- 在使用完之後,對其指派為 null 或者重新配置設定
7.2 被忘記的 Timers或者 callbacks
在 JavaScript 中使用 setInterval 非常常見
大多數庫都會提供觀察者或者其它工具來處理回調函數,在他們自己的執行個體變為不可達時,會讓回調函數也變為不可達的。對于 setInterval,下面這樣的代碼是非常常見的:
var serverData = loadData();
setInterval(function() {
var renderer = document.getElementById('renderer');
if(renderer) {
renderer.innerHTML = JSON.stringify(serverData);
}
}, 5000); //This will be executed every ~5 seconds.
這個例子闡述着timers 可能發生的情況:計時器會引用不再需要的節點或資料
7.3 閉包:一個可以通路外部(封閉)函數變量的内部函數
JavaScript 開發的一個關鍵方面就是閉包:一個可以通路外部(封閉)函數變量的内部函數。由于 JavaScript 運作時的實作細節,可以通過以下方式洩漏記憶體:
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing) // a reference to 'originalThing'
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log("message");
}
};
};
setInterval(replaceThing, 1000);
7.4 DOM引用
有時候,在資料結構中存儲 DOM 結構是有用的。假設要快速更新表中的幾行内容。将每行DOM 的引用存儲在字典或數組中可能是有意義的。當這種情況發生時,就會保留同一 DOM 元素的兩份引用:一個在 DOM 樹種,另一個在字典中。如果将來某個時候你決定要删除這些行,則需要讓兩個引用都不可達。
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image')
};
function doStuff() {
elements.image.src = 'http://example.com/image_name.png';
}
function removeImage() {
// The image is a direct child of the body element.
document.body.removeChild(document.getElementById('image'));
// At this point, we still have a reference to #button in the
//global elements object. In other words, the button element is
//still in memory and cannot be collected by the GC.
}
8、說說你對原型(prototype)了解(必會)
JavaScript 是一種通過原型實作繼承的語言與别的進階語言是有差別的,像 java,C#是通過類型決定繼承關系的,JavaScript 是的動态的弱類型語言,總之可以認為 JavaScript 中所有都是對象,在 JavaScript 中,原型也是一個對象,通過原型可以實作對象的屬性繼承,JavaScript 的對象中都包含了一個” prototype”内部屬性,這個屬性所對應的就是該對象的原型
“prototype”作為對象的内部屬性,是不能被直接通路的。是以為了友善檢視一個對象的原型,Firefox 和 Chrome 核心的 JavaScript 引擎中提供了”proto“這個非标準的通路器(ECMA 新标準中引入了标準對象原型通路器”Object.getPrototype(object)”)
原型的主要作用就是為了實作繼承與擴充對象
9、介紹下原型鍊(解決的是繼承問題嗎)(必會)
JavaScript 原型: 每個對象都會在其内部初始化一個屬性,就是 prototype(原型)
原型鍊:當我們通路一個對象的屬性時,如果這個對象内部不存在這個屬性,那麼他就會去 prototype 裡找這個屬性,這個 prototype 又會有自己的 prototype,于是就這樣一直找 下去,也就是我們平時所說的原型鍊的概念
特點:JavaScript 對象是通過引用來傳遞的,我們建立的每個新對象實體中并沒有一份屬于自己的原型副本。當我們修改原型時,與之相關的對象也會繼承這一改變
10、簡單說說 js中的繼承(必會)
繼承有以下六種方法:
- 1、原型鍊繼承 JavaScript 實作繼承的基本思想:通過原型将一個引用類型繼承另一個引用 類型的屬性和方法
- 2、借用構造函數繼承(僞造對象或經典繼承) JavaScript 實作繼承的基本思想:在子類構造函數内部調用超類型構造函數。 通過使用 apply() 和 call() 方法可以在新建立的子類對- 象上執行構造函數
- 3、組合繼承(原型+借用構造)(僞經典繼承) JavaScript 實作繼承的基本思想:将原型鍊和借用構造函數的技術組合在一塊,進而發揮兩者之長的一種繼承模式将原型鍊和借用構造函- 數的技術組合到一起,進而取長補短發揮兩者長處的一種繼承模式
- 4、型式繼承 JavaScript 實作繼承的基本思想:借助原型可以基于已有的對象建立新對象,同時還不必須是以建立自定義的類型
- 5、寄生式繼承 JavaScript 實作繼承的基本思想:建立一個僅用于封裝繼承過程的函數,該 函數在内部以某種方式來增強對象,最後再像真正是它做了所有工作一樣傳回對象。寄生- 式繼承是原型式繼承的加強版
- 6、寄生組合式繼承 JavaScript 實作繼承的基本思想:通過借用函數來繼承屬性,通過原型 鍊的混成形式來繼承方法