js高程读书笔记

252 阅读13分钟

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";