js高程讀書筆記

莫修發表於2018-04-09

ECMAScript

  1. NaN與任何運算元進行比較,都返回false
NaN < 3 // false
NaN >= 3 // false
NaN === NaN // false
NaN === 1 // false
"a" < 3 // false 
"a" >=3 // false 
複製程式碼

2.邏輯與操作

  • 如果有任何一個運算元是null,則返回null
  • 如果有任何一個運算元是undefined,則返回undefined
  • 如果有任何一個運算元是NaN,則返回NaN
null && true // null
true && null // null
undefined && true // undefined
true && undefined // undefined
NaN && true // NaN
true && NaN // NaN
複製程式碼

3.邏輯或操作

  • 如果兩個運算元都為null,則返回null
  • 如果兩個運算元都為undefined,則返回undefined
  • 如果兩個運算元都為NaN,則返回NaN
null || null // null
undefined || undefined // undefined
NaN || NaN // NaN
false || null // null
false || undefined // undefined
false || NaN // NaN
true || null // true
true || undefined // true
true || NaN // true
複製程式碼

4.null與undefined相等但不全等

null == undefined // true 
null === undefined // false
複製程式碼

5.label語句

var num = 0;
outermost:
for (var i=0; i < 10; i++) {
 for (var j=0; j < 10; j++) {
 if (i == 5 && j == 5) {
 break outermost;
 }
 num++;
 }
}
alert(num); //55 

var num = 0;
outermost:
for (var i=0; i < 10; i++) {
 for (var j=0; j < 10; j++) {
 if (i == 5 && j == 5) {
 continue outermost;
 }
 num++;
 }
}
alert(num); //95 
複製程式碼
  1. 函式引數按值傳遞。 但當引數是一個物件時,即 使這個引數是按值傳遞的,引數也會按引用來訪問同一個物件。
function addTen(num) {
 num += 10;
 return num;
} 
var num=1;
addTen(num);
console.log(num); // 1

function setName(obj) {
 obj.name = "Nicholas";
}
var person = new Object();
setName(person);
console.log(person.name); //"Nicholas" 
複製程式碼

實際上,當在函式內部重寫 obj 時,這 個變數引用的就是一個區域性物件了。而這個區域性物件會在函式執行完畢後立即被銷燬。

function setName(obj) {
 obj.name = "Nicholas";
 obj = new Object();
 obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas" 
複製程式碼

6.sort方法預設按升序排列,會呼叫每個陣列項的toString()方法,然後比較字串。 也可以給sort傳遞一個比較函式,比較函式接收兩個引數,如果第一個引數應該位於第二個之前則返回一個負數,如果兩個引數相等 則返回 0,如果第一個引數應該位於第二個之後則返回一個正數

var values = [0, 1, 5, 10, 15];
values.sort();
console.log(values); // [0,1,10,15,5] 

values.sort(function(a,b){
    return a-b
})
console.log(values); // [0,1,5,10,15]
複製程式碼
  1. Date
  • Date.now() 返回當前時間的毫秒數
  • new Date() 返回當前時間物件
  • +new Date() 返回當前時間的毫秒數,相當於Date.now()
Date.now() // 1519698546791
+new Date() // 1519698742356
複製程式碼

8.RegExp 建構函式的屬性

長屬性名 短屬性名 說明
input $_ 最近一次要匹配的字串。Opera未實現此屬性
lastMatch $& 最近一次的匹配項。Opera未實現此屬性
lastParen $+ 最近一次匹配的捕獲組。Opera未實現此屬性
leftContext $` input字串中lastMatch之前的文字
multiline $* 布林值,表示是否所有表示式都使用多行模式。IE和Opera未實現此屬性
rightContext $' Input字串中lastMatch之後的文字
$1 儲存第一個匹配的捕獲組
$2 儲存第二個匹配的捕獲組
··· ···
$9 儲存第九個匹配的捕獲組
var text = "this has been a short summer";
var pattern = /(.)hort/g;
/*
 * 注意:Opera 不支援 input、lastMatch、lastParen 和 multiline 屬性
 * Internet Explorer 不支援 multiline 屬性
 */
if (pattern.test(text)){
 console.log(RegExp.input); // this has been a short summer
 console.log(RegExp.leftContext); // this has been a
 console.log(RegExp.rightContext); // summer
 console.log(RegExp.lastMatch); // short
 console.log(RegExp.lastParen); // s
 console.log(RegExp.multiline); // false
} 
複製程式碼
var text = "this has been a short summer";
var pattern = /(.)hort/g;
if (pattern.test(text)){
 console.log(RegExp.$_); // this has been a short summer
 console.log(RegExp["$`"]); // this has been a
 console.log(RegExp["$'"]); // summer
 console.log(RegExp["$&"]); // short
 console.log(RegExp["$+"]); // s
 console.log(RegExp["$*"]); // false
} 
複製程式碼
var text = "this has been a short summer";
var pattern = /(..)or(.)/g;

if (pattern.test(text)){
 console.log(RegExp.$1); //sh
 console.log(RegExp.$2); //t
} 
複製程式碼

9.函式內部屬性

  • arguments,一個類陣列物件,包含著傳入函式中的所有引數。雖然 arguments 的主要用途是儲存函式引數, 但這個物件還有一個名叫 callee 的屬性,該屬性是一個指標,指向擁有這個 arguments 物件的函式。
function a(){
	console.log(arguments.callee)
}
a() // ƒ a(){
	//    console.log(arguments.callee)
    // }
複製程式碼
  • this 引用的是函式據以執行的環境物件——或者也可以說是 this 值(當在網頁的全域性作用域中呼叫函式時, this 物件引用的就是 window)
window.color = "red";
var o = { color: "blue" };
function sayColor(){
 console.log(this.color);
}
sayColor(); //"red",全域性呼叫函式,this指向全域性window
o.sayColor = sayColor; 
o.sayColor(); //"blue", 物件中呼叫函式,this指向該物件
var test=o.sayColor;
a(); // "red", 全域性中呼叫,this指向全域性
複製程式碼
  • caller這個屬性中儲存著呼叫當前函式的==函式==引用, 如果是在全域性作用域中呼叫當前函式,它的值為 null.
function outer(){
 inner();
}
function inner(){
 console.log(inner.caller);
}
outer(); // ƒ outer(){
         //    inner();
         // }
         
// 更鬆散的耦合
function outer(){
 inner();
}
function inner(){
 console.log(arguments.callee.caller);
}
outer(); // ƒ outer(){
         //    inner();
         // }
inner(); // null,如果是在全域性作用域中呼叫當前函式,caller它的值為 null
複製程式碼

10.encodeURI,encodeURIComponent

  • encodeURI() 主要用於整個URI,encodeURI()==不會==對本身屬於 URI 的特殊字元進行編碼,例如冒號、正斜槓、 問號和井字號
  • encodeURIComponent(),主要用於對 URI 中的某一段, encodeURIComponent()會對它發現的任何非標準字元進行編碼
var uri = "http://www.wrox.com/illegal value.htm#start"; 
encodeURI(uri) // "http://www.wrox.com/illegal%20value.htm#start"
encodeURIComponent(uri) // "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start"

複製程式碼

11、最快捷的陣列求最大值,陣列去重

Math.max(...[1,2,3,4,5])
Math.max.apply(Math,[1,2,3,4,5])

//陣列去重
[...new Set([2,3,4,2,6])]
Array.from(new Set([2,3,4,2,6]))
複製程式碼

12、判斷訪問一個物件的屬性是訪問的是否是原型上的屬性

function hasPrototypeProperty(obj,name){
    /* in 操作符在單獨使用時,會在通過物件能夠訪問給定屬性時返回 true,無論該屬性存在於例項中還是原型中;
     * 而hasOwnProperty()只在屬性存在於例項中時才返回 true
     */
    return !obj.hasOwnProperty(name) && (name in obj);   
}

// 例子
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
 alert(this.name);
};
var person = new Person();
console.log(hasPrototypeProperty(person, "name")); //true
person.name = "Greg";
console.log(hasPrototypeProperty(person, "name")); //false 

複製程式碼

13、for-in

  • 在使用 for-in 迴圈時,返回的是所有能夠通過物件訪問的、可列舉的(enumerated)屬性,其中 既包括存在於例項中的屬性,也包括存在於原型中的屬性。遮蔽了原型中不可列舉屬性(即將 [[Enumerable]]標記為 false 的屬性),例項屬性也會在 for-in 迴圈中返回,因為根據規定,所有開發人員定義的屬性都是可列舉的
  • 避免for-in在物件中使用
// 例子
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
 alert(this.name);
};

var person = new Person();
for(var i in person){
    console.log(i,person[i])
}
// name Nicholas
// age 29
// job Software Engineer
// sayName ƒ (){
//  alert(this.name);
// }
複製程式碼

14.Objct.keys(),取得物件上所有==可列舉==的==例項屬性==(==不包括原型屬性==)

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
 alert(this.name);
};
var keys = Object.keys(Person);
console.log(keys); //[]
keys = Object.keys(Person.prototype);
console.log(keys); //["name", "age", "job", "sayName"]

var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
console.log(p1keys); // ["name", "age"]
複製程式碼

15、組合繼承

  • 組合繼承避免了原型鏈和借用建構函式的缺陷,融合了它們的優點,成為 JavaScript 中最常用的繼 承模式。而且,instanceof 和 isPrototypeOf()也能夠用於識別基於組合繼承建立的物件。
function SuperType(name){
 this.name = name;
 this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
 alert(this.name); 
};
function SubType(name, age){
 //繼承屬性,借用建構函式模式
 SuperType.call(this, name);

 this.age = age;
}
//繼承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
 alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27 
複製程式碼

16、原型式繼承

var person = {
 name: "Nicholas",
 friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 


var a=Object.create(person);
// 相當於
var a=new Object();
a.__proto__=person;


// Object.create()方法的第二個引數與Object.defineProperties()方法的第二個引數格式相同:每個屬性都是通過自己的描述符定義的。
var person = {
 name: "Nicholas",
 friends: ["Shelby", "Court", "Van"]
}; 
var anotherPerson = Object.create(person, {
 name: {
 value: "Greg"
 }
});

alert(anotherPerson.name); //"Greg" 
複製程式碼

node.js事件

// 也可以通原型的方式非常容易的將其新增到自己的建構函式中
var EventEmitter=require('events').EventEmitter,
    A=function(){};

// 原型
A.prototype.__proto__ = EventEmitter.prototype;

// 或使用比較新的寫法
// A.prototype=Object.create(EventEmitter.prototype);

//所有A的例項都具備了事件功能
var a=new A();

a.on('eventName',function(arg1,arg2){
    // do something
    console.log('事件回撥,接受到引數:'+arg1+"  "+arg2);
})

// 分發事件,並傳入引數
a.emit('eventName','引數1','引數2');

console.log(Object.getPrototypeOf(a));
複製程式碼

17、寄生組合式繼承:是引用型別最理想的繼承正規化

function inheritPrototype(subType,superType){
    var prototype=superType.prototype;
    prototype.constructor = subType;
    subType.prototype = prototype;
}

function SuperType(name){
 this.name = name;
 this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
 alert(this.name);
};

function SubType(name, age){
 SuperType.call(this, name);

 this.age = age;
}

inheritPrototype(SubType, SuperType); 
SubType.prototype.sayAge = function(){
 alert(this.age);
}; 

instance=new SubType('xiaoming',18)
複製程式碼

18、閉包:指有權訪問另一個函式作用域中的變數的函式

  • 建立閉包的常見方式,就是在一個函式內部建立另一個函式
  • 閉包中,外部函式中的活動物件(定義的變數等)在函式執行完畢後不會銷燬,因為內部函式的作用域鏈仍然在引用外部函式的變數
  • 副作用:記憶體洩漏;外部函式中的活動物件在函式執行完畢後不會銷燬,因此會導致記憶體洩漏,可以手動解除變數引用來確保正常回收其佔用的記憶體,從而解決記憶體洩漏
  • 副作用:即閉包只能取得包含函式中任何變數的最後一個值
  • this 物件是在執行時基於函式的執行環境繫結的:在全域性函式中,this 等於window,而當函式被作為某個物件的方法呼叫時,this 等於那個物件。不過,匿名函式的執行環境具有全域性性,因此其 this 物件通常指向 window。
 // 外部函式
function createComparisonFunction(propertyName) {

 /*
  * 內部函式(一個匿名函式),訪問了外部函式中的變數propertyName
  * 之所以還能夠訪問這個變數,是因為內部函式的作用域鏈中包含createComparisonFunction()的作用域
  * 函式執行完畢後,外部函式的活動物件(變數propertyName等)不會被銷燬,因為內部函式作用域鏈仍然在引用這個變數;除非內部函式被銷燬,propertyName才會被銷燬
  */
 return function(object1, object2){
     var value1 = object1[propertyName];
     var value2 = object2[propertyName];
    
     if (value1 < value2){
        return -1;
     } else if (value1 > value2){
        return 1;
     } else {
        return 0;
     }
 };
} 

// 副作用:閉包只能取得包含函式中任何變數的最後一個值
function createFunctions(){
     var result = new Array();
     for (var i=0; i < 10; i++){
         result[i] = function(){ // 內部函式(一個匿名函式)
            return i; // i只取最後一個值10
         };
     }
     
    return result;
} 
// 解決這一副作用:再建立另一個匿名函式包裹內部函式
function createFunction(){
    var result=new Array();
    for(var i=0;i<10;i++){
        result[i]=function(num){ // 匿名函式1
            return function(){ // 匿名函式2
                return num;
            }
        }(i) // 立即執行,函式引數是按值傳遞的(引用型別傳的是指標)
    }
    
    return result;
}

// this
var name = "The Window";
var object = {
     name : "My Object",
     getNameFunc : function(){
         return function(){
            return this.name; // this指向全域性作用域,即window
         };
     }
};
alert(object.getNameFunc()()); //"The Window"

var name = "The Window";
var object = {
     name : "My Object",
     getNameFunc : function(){ 
        var that=this;//this指向物件object。 函式執行完後,變數that不會被銷燬(不管變數that變有沒有被使用過)
        return function(){
            // this指向全域性作用域window,變數that指向物件object。
            return that.name 
        }
        
     }
}

alert(object.getNameFunc()()); //"My Object" 

// 解決閉包導致的記憶體洩漏
function assignHandler(){
     var element = document.getElementById("someElement");
      // 只要匿名函式存在,element的引用數至少也是1,因此它所佔用的記憶體就永遠不會被回收,導致記憶體洩漏
     element.onclick = function(){
         alert(element.id);
     };
} 

function assignHandler(){
     var element = document.getElementById("someElement"),
              id = element.id;

     element.onclick = function(){
         alert(id);
     };
     
     element = null; // 解除對element的引用,確保正常回收element佔用的記憶體 (id仍然不會被銷燬)
} 


function F1() {
    var a = 100
    return function () {
        console.log(a)
    }
}
function F2(f1) {
    var a = 200
    console.log(f1())
}
var f1 = F1()
F2(f1) // 輸出: 100

複製程式碼

19、重新申明變數

  • js重新申明變數,只會對後續的宣告視而不見(不過,它會執行後續宣告中的變數初始化,變數宣告提升)
var a=1;
var a;
console.log(a); // 1

var b=1;
var b=2;
console.log(b); // 2

複製程式碼

20、特權方法:有權訪問私有變數的公有方法

  • 使用建構函式模式實現自定義型別的特權方法
  • 使用原型模式來實現自定義型別的特權方法
// 建構函式模式
function MyObject(){
     //私有變數和私有函式
     //privateVariable由所有例項共享。
     var privateVariable = 10;
     function privateFunction(){
        return false;
     }
     //特權方法
     //閉包,有權訪問私有變數privateVariable;在MyObject建構函式外部,沒有任何方法能訪問到privateVariable
     this.publicMethod = function (){ 
        privateVariable++;
        console.log(privateVariable)
        return privateFunction();
     };
} 
var t=new MyObject();
t.publicMethod(); // 11 false

var h=new MyObject();
t.publicMethod(); // 12 false

function Person(name){
 this.getName = function(){
    return name;
 };
 //閉包,有權訪問私有變數name;在Person建構函式外部,沒有任何方法能訪問到name
 this.setName = function (value) {
    name = value;
 };
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"


// 原型模式
(function(){

     //私有變數和私有函式
     //privateVariable由所有例項共享。
     var privateVariable = 10;
     function privateFunction(){
        return false;
     }
     //建構函式
     //沒有使用 var 關鍵字,總是會建立一個全域性變數
     MyObject = function(){
     };
     //公有/特權方法
     MyObject.prototype.publicMethod = function(){
        privateVariable++
        console.log(privateVariable);
        return privateFunction();
     };
})(); // 立即執行

var t=new MyObject();
t.publicMethod(); // 11 false

var h=new MyObject();
t.publicMethod(); // 12 false
複製程式碼

BOM

1、跨瀏覽器取得頁面視口VIEW大小(瀏覽器去掉工具欄的可見大小)

var pageHeight=window.innerHeight,
    pageWidth=window.innerWidth;
    
if(typeof pageHeight !== "number"){
    if(document.compatMode === 'CSS1Compat'){
        pageHeight=document.documentElement.clientHeight;
        pageWidth=document.documentElement.clientWidth;
    }else{
        pageHeight=document.body.clientHeight;
        pageWidth==docuemnt.body.clientWidth;
    }
}
複製程式碼

DOM

2、偏移量offsetLeft、offsetTop、offsetHeight、offsetWidth

  • offsetLeft(包含marginLeft)和offsetTop(包含marginTop)是相對於offsetParent的
  • offsetParent不一定是parentNode,例如<td>的offsetParent是<table>,因為<table>是DOM中距離<td>最近的一個有大小的元素
  • offsetHeight = height+ paddingTop + paddingBottom + borderTop + borderBottom
  • offsetWith = width + paddingLeft + paddingRight + borderLeft + borderRight
// 取得某個元素在頁面上的偏移量:將這個元素的offsetLeft和offsetTop與其offsetParent的相同屬性相加,如此迴圈至根元素,就可以得到一個基本的值
function getElementLeft(element){
    var actualLeft=element.offsetLeft,
        current=element.offsetParent;
    
    while (current !=null){
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }
    
    return actualLeft;
}

function getElementTop(element){
     var actualTop = element.offsetTop,
         current = element.offsetParent;
         
     while (current !== null){
         actualTop += current. offsetTop;
         current = current.offsetParent;
     }
     
     return actualTop;
} 
複製程式碼

3、客戶區大小clientHeight、clientWidht,客戶區大小就是元素內部的空間大小,因此滾動條佔用的空間不計算在內。 參見瀏覽器視口大小

  • clienHeight = height + paddingTop + paddingBottom
  • clientWidth = width + paddingLeft + paddingRight
  • 如果有滾動條,要去掉滾動條佔用的空間
  • 對於不包含滾動條的頁面而言, scrollWidth 和 scrollHeight 與 clientWidth 和 clientHeight 之間的關係並不十分清晰

4、滾動大小

  • scrollHeight:在沒有滾動條的情況下,元素內容的總高度(margin+border+padding+height)。
  • scrollWidth:在沒有滾動條的情況下,元素內容的總寬度(margin+border+padding+width)。
  • scrollWidth 和 scrollHeight 主要用於確定元素內容的實際大小
  • scrollLeft:被隱藏在內容區域左側的畫素數。通過設定這個屬性可以改變元素的滾動位置。
  • scrollTop:被隱藏在內容區域上方的畫素數。通過設定這個屬性可以改變元素的滾動位置
  • 確定==文件的總高度==時(包括基於視口的最小高度時),必須取得 scrollWidth/clientWidth 和 scrollHeight/clientHeight 中 的最大值,才能保證在跨瀏覽器的環境下得到精確的結果
var docHeight = Math.max(document.documentElement.scrollHeight,document.documentElement.clientHeight);

var docWidth = Math.max(document.documentElement.scrollWidth,document.documentElement.clientWidth); 
複製程式碼

3、onload事件

  • window的onload事件會在文件載入完畢(文件的css、img、JavaScript下載並執行完畢)後才會觸發
// index.js
console.log('語句 1', new Date().getTime())

// index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script src="http://localhost:3000/index.js"></script>
    <script>
        console.log('語句 2', new Date().getTime())
    </script>
    <script>
        console.log('語句 3', new Date().getTime())
            /*
             * window的onload事件會在文件載入完畢(文件的css、img、JavaScript下載並執行完畢)後才會觸發
             * 故該事件中的語句會最後執行
             */
        window.onload = function() {
            console.log('語句 4:window.onload事件中的語句', new Date().getTime())
        }
        console.log('語句 5', new Date().getTime())
    </script>
    <script>
        console.log('語句 6', new Date().getTime())
    </script>
</body>

</html>

// 控制檯輸出
語句 1 1520994942124
語句 2 1520994942127
語句 3 1520994942127
語句 5 1520994942127
語句 6 1520994942128
語句 4:window.onload事件中的語句 1520994942128
複製程式碼
  • Image物件的onload事件:新影象元素不一定要從新增到文件後才開始 下載,只要設定了 src 屬性就會開始下載
 var image = new Image();
 // 事件處理程式必須放在src屬性新增之前
 image.onload = function(event){ 
    console.log("Image loaded!");
 };
 // image新增了src屬性之後檔案就開始下載
 image.src = "smile.gif"; 
複製程式碼

相關文章