javascript高級程序設計總結二

一、面向對象的程序設計

1. 屬性類型

// 1.數據屬性 object.defineproperty(屬相所在的對象,屬性名,(4種))
    let person = {};
    Object.defineProperty(person, "name", {
        configurable: true,  //表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性
        enumerable: true,  //表示能否通過for-in循環返回屬性
        writable: true,  // 表示是否能修改屬性的值
        value: "xujiang" // 屬性的值
    })
    /* 在調用Object.defineProperty()方法創建一個新屬性時,如不指定前三個屬性字段,默認值都爲false, 如果是修改已定義的屬性時,則沒有此限制 */

// 2.訪問器屬性get/set
    let person = {name: "xujaijgn", year: 11};
        Object.defineProperty(person, "_name", {
            get: function(){
                return this.name
            },
            // 定義Set時,則設置一個屬性的值時會導致其他屬性發生變化
            set: function(newValue){
                this.name = newValue;
                this.year = 12;
            }
        })

// 定義多個屬性
Object.defineProperties(book, {
    _year: {
        writable: false,
        value: 2001
    },
    year: {
        get: function(){
            return _year
        },
        set: function(newValue){
            this._year = newValue;
        }
    }
})

2.創建對象

    原型理解:
     1.isPrototypeOf()-------確認對象之間有沒有原型關係
     2.Object.getPrototypeOf()-------獲取實例對象的原型
     3.我們不能通過實例重寫原型中的值,只能訪問,並且如果實例和原型有相同的屬性名,則會覆蓋原型中的屬性。
     4.hasOwnProperty()-----檢測一個屬性是否在實例中
     5.原型與in操作符:“name" in person:對象能訪問到給定屬性時返回true
     6.Object.key(obj)---返回一個可以包含所有可枚舉的屬性
     7.Object.getOwnPropertyNames() ---返回所有的實例屬性,包括不可枚舉的
     8.實例中的指針只指向原型,而不是構造函數。 var person1=new Person() 那person1中的指針只指向Person.prototype 即 person1._proto_=Person.prototype   
  1. 組合(構造函數模式和原型模式):用構造函數定義實例屬性,用原型定義方法和共享屬性。

3.繼承

1.原型鏈的問題
    1.包含引用類型值的原型屬性會被所有實例共享,在通過原型實現繼承時,原型實際上會變成另一個類型的實例,原先的實例屬性變成了現在的原型屬性。
    2.在創建子類型的實例時,無法向父類構造函數傳遞參數
    function Parent(name){
    this.name=name;
}
function  Child(){
}
Child.prototype=new Parent('zhangsan');
var p=new Chlid();//構造出來子類的實例沒有辦法向父類傳參。
p.name;//可以繼承父類的屬性和方法

2.借用構造函數(在子類型構造函數的內部調用父類構造函數)
    //此時實例不會共享屬性
    function Parent(name){
        this.colors = [1,3,4];
        this.name = name;
    }
    function Child(name){
        Parent.call(this, name);//但是實例化的子類能向父類傳參。
        this.age = 12;
    }
    // 存在的問題: 1.函數無法複用 2.父類的原型對於子類是不可見的,子類不能繼承原型上的屬性和方法

3.組合繼承(使用原型鏈繼承原型屬性和方法,使用借用構造繼承實例屬性) ---最常用的繼承模式
    缺點:無論如何都會調用兩次父類構造函數
    // 父類
    function Parent(name){
        this.name = "xujaing";
        this.age = 12;
    };
    Parent.prototype.say = function() { console.log(this.age) };
    // 子類繼承父類
    function Child(name){
        Parent.call(this, name);//1
        this.age = 13;
    }
    Child.prototype = new Parent();//2
    Child.prototype.constructor = Child;
    Child.prototype.say = function() { alert(this.age) };

4.原型式繼承
    實現1.
        function object(o){
            function F(){};
            F.prototype = o;
            return new F()
        }
    實現2.通過Object.create(prototype, properties) // 第一個參數爲創建新對象原型的對象,第二個參數爲對新對象定義額外屬性的對象(和defineProperties方法的第二個參數格式相同)
  A=Object.create(person, {//A._proto_=Person
        name: {
            value: "xujiang"
        }
    })
5.寄生組合式繼承(通過借用構造函數繼承屬性,通過原型鏈混成的方式繼承方法)---最理想的繼承範式
    function inheritPrototype(sub,sup){
        let prototype = Object.create(sup.prototype);//prototype._proto_=sup.prototype 
        prototype.constructor = sub;
        sub.prototype = prototype;
    }

    function Sup(){}
    Sup.prototype.say = function(){}
    function Sub(arg){
        // 關鍵
        Sup.call(this,arg);//var sub=new sup()
    }
    // 關鍵
    inheritPrototype(Sub, Sup);

二、函數表達式

閉包與變量

閉包:可以訪問定義它們的外部函數的參數和變量(除了this和arguments)。
閉包的優點:封裝私有變量,形成塊級作用域。
缺點:內存泄漏佔用內存。

function a(){
    let el = $("#el");
    let id = el.id;
    el.click(function(){
        alert(id)
    })
    // 清空dom,釋放內存
    el = null;
}

三、BOM對象

Location對象:
// location即是window對象的屬性也是document對象的屬性
1. hash // "#contents" 返回url的hash,如果不包含返回空
2. host // "www.wrox.com:80" 返回服務器名稱和和端口號
3. hostname // "www.wrox.com" 返回不帶端口號的服務器名稱
4. href // 返回當前加載頁面的完整url
5. pathname // "/a/" 返回url中的目錄或文件名
6. port // "8080" 返回url中指定的端口號
7. protocol // "http" 返回頁面使用的協議
8. search // "?q=java" 返回url中查詢字符串,以問號開頭


navigator對象:
navigator.language // "zh-CN" 瀏覽器的主語言
navigator.appName  // "Netscape" 完整的瀏覽器名稱
navigator.appVersion // 瀏覽器的版本
// 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36
navigator.cookieEnabled // true 表示cookie是否啓用
navigator.javaEnabled() // 表示瀏覽器是否啓用java
navigator.onLine // true 表示瀏覽器是否連接到了因特網
navigator.platform // "Win32" 瀏覽器所在的系統平臺
navigator.userAgent // 瀏覽器用戶代理字符串
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36"
navigator.plugins // 檢測瀏覽器中安裝的插件的數組
history對象:
1. history.go(0 | [123] | -1 | str) // 如果是Str,則會跳轉到歷史記錄中包含該字符串的第一個位置
2. history.back() //後退一頁
3. history.forward() //前進一頁
4. history.length // 保存着歷史紀錄的數量

四、DOM對象

1.將對象轉化成數組 Array.prototype.slice.apply(arguments,[0]);
2.將字符串轉化成數組:str.split(’ ')//split轉化成字符串數組

1.appendChild() //用於向childNodes末尾添加一個節點,返回新增的節點,如果節點已存在,那麼就是從原來的位置移動到新位置
2.insertBefore() //將節點插入指定位置,接收兩個參數,要插入的節點和作爲參照的節點,返回插入的節點
3.replaceChild() //替換指定節點,接收2個參數,要插入的節點和要替換的節點,返回被移除的節點
4.removeChild() //移除節點,返回被移除的節點
5.cloneNode([true]) //參數爲true,執行深複製,複製節點及整個子節點,爲false時複製節點本身。cloneNode不會複製節點的javascript屬性,但IE在此存在一個bug,所以建議在複製之前最好先移除事件處理程序
document類型:
1. document的節點類型nodeType的值爲92. document.documentElement // 取得對<html>的引用
3. document.body // 取得對body的引用
4. document.title // 取得文章標題
5. document.title = "xxx" //設置文章標題
6. document.URL //取得完整的url
7. document.domain //取得域名
8. document.referrer //取得來源頁面的url
element類型:
1.nodeType值爲:1
2.nodeName的值爲元素標籤名
3.tagName // 元素標籤名,返回大寫值,比較時一般採用 element.tagName.toLowerCase()
4.取得元素屬性 getAttribute() / setAttribute() / removeAttribute()
// 注:自定義屬性通過點語法訪問時會返回undefined
5.attributes // 獲取元素的屬性集合,訪問方法: element.attributes[i].nodeName / element.attributes[i].nodeValue
6.創建元素 // document.createElement("div" | "<div class=\"box\">aaa</div>")
7.創建文本子節點 // document.createTextNode("Hello world")

五、元素大小

1.偏移量

offsetParent:獲取元素的最近的具有定位屬性(absolute或者relative)的父級元素。如果都沒有則返回body
offsetHeight:元素在垂直方向上佔用的空間大小。包括元素的高度、(可見的)水平滾動條的高度、上邊框高度和下邊框高度。(不包括外邊框margin)
offsetWidth:元素在水平方向上佔用的空間大小。包括元素的寬度、(可見的)垂直滾動條的寬度、左邊框寬度號右邊框寬度。
offsetLeft:元素的左外邊框至父元素的左內邊框之間的像素距離。
offsetTop:元素的上外邊框至父元素的上內邊框之間的像素距離。
偏移量

2.客戶區的大小

clientWidth:元素內容區寬度加上左右內邊距寬度。可以通過document.body。clientWidth來獲取瀏覽器視口的大小。
clientHeight:元素內容區高度加上上下內邊距高度
在這裏插入圖片描述

3.滑動大小

scrollHeight:元素內容的實際總高度
scrollWidth:元素內容實際總寬度
scrollLeft:被隱藏的內容區域左側的像素數。通過設置這個屬性可以改變元素的滾動的位置
scrollTop:被隱藏的內容區域上方的像素數。通過設置這個屬性可以改變元素的滾動位置
在這裏插入圖片描述

六、事件

1.事件對象event

1. 屬性或方法
    type // 被觸發的事件類型
    target // 事件的目標 觸發事件的元素li
    currentTarget // 事件處理程序當前正在處理事件的那個元素 邦定事件的元素ul
    注: 在事件處理程序內部,對象this始終等於currentTarget的值,而target只包含事件的實際目標
    *** 一個函數處理多個事件可以使用switch(event.type)的方式
    event.preventDefault() // 阻止事件的默認行爲
    event.stopPropagation() // 阻止事件冒泡

2.事件類型

1.鼠標和滾輪事件
    1.客戶區座標位置clientX/clientY  //表示事件發生時鼠標指針在視口中的水平和垂直位置
    2.頁面座標位置 pageX/pageY  //表示事件在頁面中發生的位置
    3.屏幕座標位置 //獲取事件發生時在屏幕中的位置

2.修改鍵(如果用戶在觸發事件時按下了shift/ctrl/alt/Meta,則返回true)
    event.shiftkey | event.altKey | event.metaKey | event.ctrlKey

3.鼠標按鈕(event.button)
// 對於mousedown和mouseup,其event中存在一個button屬性,值爲0表示主鼠標按鈕,1表示中間鼠標按鈕,2表示次鼠標按鈕

4.鼠標滾輪事件(mousewheel)
    1.兼容方案:
        let getWheelDelta = function(event){
            let wheelDelta = event.wheelDelta ? event.wheelDelta : (-event.detail * 40);
            return wheelDelta
        }
    *** 注:document在普通瀏覽器中通過mousewheel監聽鼠標滾輪事件,在火狐中使用DOMMouseScroll監聽

5.鍵盤與文本事件
6.變動事件
    1.DOMSubtreeModified | DOMNodeInserted | DOMNodeRemoved
    *例子
        el.addEvent("DOMSubtreeModified", fn1)

7.HTML5事件
    1.contextmenu事件(自定義上下文菜單)
    2.DOMContentLoaded事件(在形成完整dom樹之後就觸發,不理會圖像,js文件,css文件等資源是否下載完成)
    3.hashchange事件(在URL的參數列表發生變化【即#號後面的所有字符串】時觸發)
    注:必須要把hashchange添加給window對象,event對象包含兩個屬性oldURL和newURL,分別保存着參數列表變化前後的完整URL  vue的路由就是這個機制 通過haschange來監聽url的變化,實現url改變但是不刷新頁面的多頁面的效果
    // 例子
    window.addEvent("hashchange", function(event){
        // oldURL和newURL存在兼容問題,最好用location.hash代替
        console.log(event.oldURL, event.newURL);
    })

3.性能問題

當你卸載頁面時,事件處理佔用的內存沒有釋放。所以在卸載之前通過unonload事件移除所有事件的處理程序。並且不會保存在緩存中。

七、跨域文檔傳遞iframe

// 源頁面
window.onload = function(){
    // 獲取源頁面iframe的內容window對象
    var iframeWindow = document.querySelector("#iframe").contentWindow;
    // 向iframe發送消息,並指定源的地址,兩個參數必填
    iframeWindow.postMessage("xujiang", "http://127.0.0.1:5500");

    var mesWrap = document.querySelector(".mes-wrap");
    // 接收iframe傳來的消息
    window.addEventListener("message",function(e){
    // alert(e.data);
        mesWrap.innerHTML = e.data;
        iframeWindow.postMessage("你叫什麼?", "http://127.0.0.1:5500");
    },false);
}

// iframe頁面,監聽其他域傳來的消息
window.addEventListener("message",function(e){
    // 向發送消息的域反饋消息,event對象的屬性如下:
    // data 傳入的字符串數據
    // origin 發送消息的文檔所在的域
    // source 發送消息的文檔的window的代理
    e.source.postMessage("hello", "http://127.0.0.1:5500");
},false);

八、ajax和cors

// ajax
var xhr = new XMLHttpRequest(); // 創建xhr對象

// 第一個方法:open(get | post等, "exam.php", false) 參數爲請求類型,請求url,是否異步的boolean
xhr.open("get","exam.php", false); // 調用該方法並不是真正的請求,而是請求一個請求以備發送

// 發送真正的請求,接收一個參數,即作爲請求主體要發送的數據,不發送數據時必須傳遞null,因爲對於某些瀏覽器來說該參數是必須的
xhr.send(null)

// 檢驗響應的狀態--->針對同步
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
    var data = xhr.responseText;
}else{
    console.log(xhr.status);
}

// 異步方案
xhr.onreadystatechange = function(){
    // xhr.readyStatus表示請求/響應過程的當前活動階段
    // 0 未初始化,還沒調用open()
    // 1 啓動,已調用open()方法但未調用send()
    // 2 發送, 已調用send()方法,但未收到響應
    // 3 接收,已接收到部分響應數據
    // 4 完成,已接受到全部響應數據
    if(xhr.readyStatus == 4){
        if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
            var data = xhr.responseText;
        }else{
            console.log(xhr.status);
        }
    }
}
xhr.open("get","exam.php", false);
xhr.send(null);

// 在接收到響應之前還可以取消異步請求
xhr.abort() // 在停止請求之後還應該進行解引用操作,防止內存堆積

// 設置http請求頭,必須放在open和send中間
xhr.open("get","exam.php", false);

xhr.setRequestHeader("accept", "application/json; charset=utf-8")

xhr.send(null);

// 獲取響應頭信息
xhr.getResponseheader("accept");
xhr.getAllResponseHeaders();

// get請求:向現有url中添加查詢字符串
function addUrlParam(url, name, value){
    url += (url.indexOf("?") == -1 ? "?" : "&");
    url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
}

// post請求:模擬表單提交
xhr.open("get","exam.php", false);
// 設置提交時的內容類型
xhr.setRequestHeader("content-Type", "application/x-www-form-urlencoded")
// 假設表單form對象已獲取
xhr.send(serialize(form));

// XHR2級 -- formData --序列化表單以及創建和表單格式相同的數據(用於通過xhr傳輸)
var data = new FormData();
data.append(key,value);
// 也就可以用表單元素的數據預先填入數據
var data = new FormData(document.forms[0]);
//使用FormData的好處在於不必明確地在xhr上設置請求頭部
xhr.send(new FormData(form));

// 進度事件
loadStart/progress/error/abort/load

// 跨域資源共享CORS
核心思想: 使用自定義的http頭部讓瀏覽器和服務器進行溝通,從而決定請求是成功還是失敗
原理:
1.請求頭指定源:Origin: http://www.baidu.com
2.如果服務器認爲這個請求可以接受,就在Access-Control-Allow-Origin頭部回發相同的源信息
Access-Control-Allow-Origin:http://www.baidu.com
(如果是公共資源,可以回發“*”)
3.如果沒有這個頭部,或者有這個頭部但是源信息不匹配,瀏覽器就會駁回請求

// 主流瀏覽器對cros的實現方式: 在url中使用絕對路徑,但有限制:不能設置自定義頭部,不能發送和接收cookie,獲取不到getAllResponseHeaders()的返回值

// 帶憑據的請求
withCredentials屬性設置爲true
// 服務器接收到帶憑據的請求後,會用下面的頭部來請求,如果響應不包含這個頭部,瀏覽器將不會把響應數據交給js
Access-Control-Allow-Credentials: true

// 跨瀏覽器的cros
function createCORSRequest(method,url){
    var xhr = new XMLHttpRequest();
    if("withCredentials" in xhr){
        xhr.open(method,url,true);
    }else if(typeof XDomainRequest != "undefined"){
        xhr = new XDomainRequest();
        xhr.open(method,url);
    }else{
        xhr = null;
    }
    return xhr
}
var req = createCORSRequest("get","http://www.baidu.com/page/");
if(req){
    req.onload = function(){
        // 對響應數據進行處理
    };
    req.send();
}
// 以上提供的公共方法有
// abort() 用於停止正在進行的請求
// onerror 用於替代onreadystatechange檢驗錯誤
// onload 用於替代onreadystatechange檢驗成功
// responseText 用於取得響應內容
// send() 用於發送請求

// 其他跨域技術
1.圖像ping---常用於跟蹤用戶點擊頁面和動態廣告曝光數,只能get請求
    var img = new Image();
    img.onload = img.onerror = function(){
        // 操作
    }
    img.src = "http://baidu.com?name=xujaing";

2.JSONP---可以直接訪問響應文本,可以在瀏覽器和服務器之間進行雙向通信,但有安全隱患
    function handleResponse(data){
        console.log(data);
    }
    var script = document.createElement("script");
    script.src = "http://a.net/json/?callback=handleResponse";
    document.body.insertBefore(script, document.body.firstChild);

3.Comet (服務器推送SSE)
    常用的技術有長輪詢和流
4.Web Sockets

九、高級技巧

1.高級函數

1.Object.prototype.toString.call(value) == "[object Array]";//檢測數據類型
2.//原生實現bind 柯理化思想 
//1.this 指向 apply /call2.參數形式 可以通過call(參數多)和apply實現(apply更方便)3.返回一個待執行的函數 閉包
function testBind(that){
  var _this=this;
  var args=Array.prototype.slice.apply(arguments,[1]);//通過slice將arguments轉換成數組,取得bind的除第一個參數以外的參數
  return function(){
  return _this.apply(that,args.concat(Array.prototype.slice.apply(arguments,[0])))
}

}

2.高級定時器

//防抖
function decouce(fn,wait){
var timer=null;
return function(){
clearTimeout(timer);
var timer=setTimeout(()=>{
     fn();

},wait);
}
}
//節流
function throttle(fn,time,wait){
var pre=null;
var timer=null;
var now=new Date();
if(pre==undifined) pre=now;
if(now-pre>time){
 clearTimeout(timer);
    fn();
    pre=now;
}else{//小於time那麼在wait內再執行一次
clearTimeout(timer)
 timer=setTimerout(()=>{
 fn();
},wait)
}
}

十、新的API

1.requestAnimationFrame():計時器不需要設置時間間隔,比其他兩種精準

(function(){
    function draw(timestamp){
        // 計算兩次重繪的時間間隔
        var drawStart = (timestamp || Date.now()),
        diff = drawStart - startTime;

        // 使用diff確定下一步的繪製時間

        // 把startTime重寫爲這一次的繪製時間
        startTime = drawStart;

        // 重繪UI
        requestAnimationFrame(draw);
    }

    var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame,
    startTime = window.mozAnimationStartTime || Date.now();
    requestAnimationFrame(draw);
})();

https://www.cnblogs.com/AI-fisher/p/11178130.html
https://zhuanlan.zhihu.com/p/84326290

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章