《JavaScript 高级程序设计》notes

Chapter 3    数据类型

  •     数值转换    把非数值转换为数值的三种函数

    1.    Number()    

Number("")    //0
Number(true)    //1
Number(null)    //0
Number(undefined)    //NaN

Number() 在转换字符串的时候比较复杂,且不合理。

    2.    parseInt()

parseInt("123abc")    //123  忽略后面的非数字字符
parseInt("070")    //70   ECMAScript5 JavaScript中已经不具备解析八进制的能力
parseInt("0xA")    //10   十六进制
parseInt("22.5")    //22    取整,小数点并不是有效的数字字符

第二个参数能够用于指定转换的时候使用多少进制

parseInt("10",2)    //2
parseInt("10",8)    //8
parseInt("10",10)    //10
parseInt("10",16)    //16

3.    parseFloat() 

parseFloat("123abc")    //123
parseFloat("22.34.5")    //22.34  字符串中的第一个小数点是有效的
parseFloat("0xA")    //0    忽略前导的0,所以只能解析十进制
parseFloat("090.8")    //90.8
  •     String类型      转换为字符串

    两者数值和布尔值的转换结果相同

    1.  toString()    返回相应字符值的字符串,还能够输出任意有效进制格式表示的字符串值

var num = 10
num.toString(8)    //"12"
    null 和 undefined没有这个方法

    2.  String() 方法

    该函数的使用,如果值有toString()方法则先调用该方法并返回相应结果。

String(null)    //"null"
String(undefined)    //"undefined"
String(true)    //"true"

    因为null 和 undefined 没有toString() 方法,所以函数返回这两个值得字面量

  •     操作符

一元加操作符:    像Number() 转型函数一样对这个值进行转换。

+"01"    //1
+"1.1"   //1.1
+false    //0

一元减操作符主要用于表示负数

-"01"    //-1
-"1.1"    //-1.1
-false    //0

可利用+、- 规范转换数据类型

"37"-7   //30   num - 0  num调用Number()方法转换成数值
"37"+7    //377  num + ""  num调用string() 或 toString() 方法

关系操作符:如果两个都是字符串,则比较对应的字符编码值

var result="Black" < "alph"    //true  B的字符编码66,a的字符编码97
"23"<"3"    //true    "2"的字符编码50,"3"的字符编码51

如果一个操作数是数值,则将另一个操作数转换成数值,notice :

    NaN < 3    //false

NaN >= 3    //false  任何数和NaN比较都是false
相等操作符:==    如果一个数是数值,则将另一个转换为数值  notice : 
null == undefined   他们是类似的值
逗号操作符:在用于赋值时,逗号操作符会返回表达式中的最后一项
num = (5,4,0)    0

Chapter 4    变量、作用域和内存问题

  • 基本数据类型和引用类型的值

js中有六种数据类型,包括五种基本数据类型(Number,String,Boolean,Undefined,Null),和一种复杂数据类型(Object)。JavaScript 变量均为对象。


原始类型(基本类型):按值访问,可以操作保存在变量中实际的值。原始类型汇总中null和undefined比较特殊。

引用类型:引用类型的值是保存在内存中的对象。* 与其他语言不同的是,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。所以引用类型的值是按引用访问的。(这种说法不严密,当复制保存着对象的某个变量时,操作是对象的引用。但在为对象添加属性时,操作的是实际的对象

函数的参数是按值传递的:但是obj 也会按引用来访问同一个对象

function setName(obj) {   
    obj.name = 'zjzhome';   
    obj = new Object();  
    obj.name = 'zjz'   
}  
var person = new Object();  
setName(person);  
console.log(person.name);   //  zjzhome  

即使在函数内部修改了参数的值,但原始的引用仍然保持未变。

  • 检测类型:typeof  和 instanceof 

typeof 对于检测基本数据类型是最佳的工具,但是检测引用类型的值时,用处不大

typeof("a")       //"string"
typeof(2)        //"number"
typeof(undefined)    //"undefined"
typeof([1,2])    //"object"
typeof(null)    //"object"   //null返回对象

obj  instanceof Object       obj原型链上是否有Object构造函数的实例

[1,2] instanceof Array    //true

使用    Object.prototype.toString.call(obj)  检测对象类型,所有类型都会得到不同的字符串,几乎完美。

垃圾回收机制:找出那些不再继续使用的变量,然后释放其占用的内存。

Chapter 5    引用类型

  • 检测数组

1.    instanceof操作符,它只能在同一个全局执行环境中判断。如果有多个框架,就存在多个不同版本的Array构造函数。

value instanceof Array;

2.    ECMAScript 5新增 Array.isArray() 方法:确定这个值是不是数组。

Array.isArray(value);

(支持的浏览器IE9+、Firefox 4+、Chrome、Safari 5+、Opera 10.5)

  • RegExp 类型

基本语法
    var expression = / pattern / flags;
        1.   g:   global: 并非在找到第一个字符串后停止
        2.   i:    case-insensitive
        3.   m:  multiline多行查找

调用

var pattern1 = new RegExp("at", "i");  //RegExp()的参数是字符串,特殊字符需要双重转义
var patter2 = /at/i;
  • Function 类型

构造方法:

Function x(a)
var fun = function(a);  //没有必要使用函数名,函数的末尾有一个分号
没有重载
    可以把函数名想象为指针。如果重名函数,后面会覆盖前面

callee&caller

    callee属性:指向拥有这个argument对象的函数,arguments.callee().

    caller属性:保存着调用当前函数的函数引用,如果在全局作用域中调用当前函数,它的值为null。

    outer() 函数在内部调用了inner函数,则inner.caller指向outer。为了实现更松散的耦合,可以使用:arguments.callee.caller

函数属性和方法

        prototype 属性是保存引用类型的实例方法的真正所在。

        每个函数都包含两个非继承而来的方法:apply() 和 call()  还能够扩充函数运行的作用域 ,对象不需要与方法有任何的耦合关系。

    sum.apply (this, [num1,num2])   参数传入的是数组。

    sum.call (this, num1, num2)  参数必须枚举。

    bind()    this值会被绑定到对象上。

  • 基本包装类型

    var obj = new Object("任何类型");

        new 出来的对象和自动创建的基本包装类型对象的生存期不同。

        new 出来的对象在执行流离开当前作用域之前移植都保存在内存中。

  但是,自动创建的类型, 仅有执行代码的瞬间的生存期,会自动执行以下操作:

    var s1 = "hello World"

        1)创建一个实例: new String("hello World") 
        2)然后删除:null
        3)下次调用s1时会再次创建对象

所以不能够给自动创建的对象加任何属性,因为下次调用的时候就没了!!!!!!

  • Number  类型
    1.  toFixed(num): 显示几个小数

    2.  toExponential(num): 显示几个指数

Chapter 6    面向对象的程序设计

ECMAScript中有两种属性:数据属性和访问器属性

  • 数据属性

4个描述其行为的特性:
    1. Configurable:     能否重新定义/删除/修改为访问器属性 
    2. Enumerable:     能否通过for-in循环返回属性
    3. Writeable:     能否修改属性的值
    4. Value:     读取和写入时都调用这个属性
通过对象的Object.defineProperty()去定义这个四个属性(如果用改方法创建***新的属性***,全部属性默认为false):

var person = ();
Object.defineProperty(person, "name", {
	writable: false,
	value:"benny"
});
  • 访问器属性

4个特性:
    1. Configurable: 表示能否通过delete删除属性从而重新定义属性。如果不是新属性,就是默认true
    2. Enumerable:    表示能否通过for-in 循环返回属性同上。如果不是新属性,就是默认true
    3. Get: 读取属性时调用。默认undefined
    4. Set: 设置属性时调用。默认undefined

var book = {
	_year: 2004, (!!!在属性前的下划线表明该属性只能通过对象方法访问)
	price: 10
}


Object.defineProperty(book, "year", {
	get: function() {
		return this._year;
	}
	set: function(newValue) {
		this._year = newValue;
		this.price +=newValue - 2;  ( !!! 通过设置一个属性的值可以导致其他属性发生变化)
	}
})
  • 工厂模式Factory Pattern

    通过function创建对象,用函数来封装以特定接口创建对象的细节。

function crateObj(name, age, job) {
	var o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;

	return o
} 
    工厂模式的优点:解决创建多个相似对象的问题
    工厂模式的缺点:没解决识别一个对象的类型
  • 构造函数模式
    1.    不显式的创建对象
    2.    直接将属性和方法赋给了this对象
    3.    没有return语句
function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName(){
		return this.name;
	}
}

调用:var person = new Person(name, age, job);(要创建Person的新实例,必须用new!!!!!!)

经历了以下步骤:
1: 创建了一个新对象
2: 将构造函数的作用域赋给新对象,this指向新对象
3: 执行构造函数中的代码:添加新属性
4: 返回新对象

    每个Person实例都包含一个不同的Function实例的本质,不同的作用域链和标识符解析,但是创建Function新实例的机制仍然相同

    不同的实例上的同名函数是不相等的:

person1.sayName == person2.sayName;    //false

但是如果把sayname()方法直接添加到Person的prototype中,两者就相等。

  • 原型对象

    当调用构造函数创建一个新实例后,该实例内部将包含一个指针(内部属性),指向构造函数的原型对象。(原型最初只包含xonstructor属性,而该属性也是共享的)

    Person.prototype.isPrototypeOf(person1)  确定是否是原型对象的实例

    person1.hasOwnProperty("name")    检测属性是否存在于实例中

    "name" in person1    无论属性存在于实例中还是原型中,都返回true

只要in操作符返回true,hasOwnProperty()返回false,就可以确定属性是原型中的属性。

  • 更简单的原型语法
function Person(){}
Person.prototype = {
    constructor:Person,     //所以,重设constructor属性,确保能够访问的属性
    sayName:function(){
        alert(this.name);
    }
}

    但是constructor属性不再指向Person,会导致[[Enumerable]]特性被设置为true。

    本质上重写了默认的prototype对象。

  • 原型的动态性

    1.    修改属性: 由于原型中查找值得过程是一次搜索,即使是先创建了实例,后修改原型也能够立即从实例上反映出来。

    2.    修改对象: 但是把原型修改成另一个对象,就等于切断了构造函数与最初原型之间的联系,就会发生错误。重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系。

!!!实例中的指针仅指向原型,而不指向构造函数。

  • 原型对象的问题

    原型中的所有属性都是被很多实例共享的。修改其中一个实例属性,另一个实例属性也会被更改。

  • 组合使用构造函数模式和原型模式

    构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

function Person(name) {
    this.name = name;
    this.friends = ["BB","AA"];    //构造函数定义实例的属性
}
Person.prototype = {
    sayName:function() {    //原型模式定义方法和共享的属性
        alert(this.name);    
    }
}
var person1=new Person();
var person2=new Person();

person1.friends.push("CC");            修改构造函数定义的实例属性
person1.friends;     ["BB", "AA", "CC"]
person2.friends;     ["BB", "AA"]
person1.sayName === person2.sayName;    //true ,引用了不同的数组,所以没有影响
  • 继承
  • 接口继承和实现继承
  • ECMAScript中只有实现继承(继承实际的方法)
fuction super() {
}
function sub() {
}
//继承
sub.prototype = new super()
var test = new sub();

Chapter 7    函数表达式

1. function say(){}; 命名函数
2. var name = funtion(){}; : 匿名函数anonymous function
    1.  函数申明:重要特征函数申明提升(function declaration hosting),在执行代码之前会先读取函数声明

    2.  函数表达式不存在函数声明提升的特征。

  • 递归

    利用arguments.callee解决递归问题,arguments.callee是指向正在执行的函数的指针

严格模式下,不能调用arguments.callee,所以只能通过命名函数来解决:

var fac = (function f(num) {
	if (num < 1) {
		return 1;
	} else {
		return num * f(num - 1);
	}
});
这种情况下即使将fac赋给其他变量,函数的名字仍然有效。
  • 匿名函数

      匿名函数(也称拉姆达函数):就是没有函数名的函数

var double = function(x) { return 2* x; }    
"="    右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量double。

  • 闭包 Closure

    闭包指的是有权访问另一个函数作用域中的变量的函数。

function create(propertyName) {
	return function(a, b) {
		var obj = a[propertyname];
		......
	}
}

某个函数变调用的时候会创建一个执行环境(execution context)以及作用域链。。

JavaScript中的匿名函数及函数的闭包:闭包内层引用父函数中的值,变量是最终值。

  • 变量的副作用
function create() {
    var res = new Array();
    for (var i = 0; i < 10; i++) {
        res[i] = function(){
            return i;
        }
    }
    return res;
}
    表面上每个函数都返回索引i的值,但是其实每个都返回10,因为当create()被调用时,产生的活动对象中会存有一个索引值i,然而每个res[i]的元素都指向同一个i,所以当结束之后,全部都是10.
res[i] = function(num) {
    return function(){
        return num
    }
}(i);
每次返回res[i]时没有用i来赋值,而是重新设定了一个num,每个函数都有一个num的副本
  • this对象

        调用函数时,活动对象会生成this和argument,搜索变量的时候只会搜索函数内部而不会访问外部函数,所以不可能拿到外部函数的this,所以此时返回this,仍是全局的this

var name = "windows"

var object = {
    var name = "obj";
    getName:function(){
        return function(){
            return this.name;
        }
    }
}

alert(object.getName()) ---> windows

在函数内部加一个var that = this,把外部作用域中的this对象保存在一个闭包能够访问到的变量里;  获取本object的this可解决这个问题;

  • 内存泄漏
function ass() {
    ver element = document.getElementById("someElement");
    element.onclick = function() {
        alert(element.id);
    };
}
        事实上这样html元素并没有被回收,因为匿名函数在调用时会保留一份ass()函数的活动对象的引用,所以只要这个匿名函数在,ass()的引用数至少是1,所以导致了内存泄漏。

    解决这个问题要消除循环引用,还要给对象赋null

var id = element.id;
......
element = null;

  • 模仿块级作用域

JavaScript没有块级作用域,作用域:scope

JavaScript不会提示后续声明重复变量。

(function(){
  })();

圆括号中的函数声明,表示一个函数表达式。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链了。

  • 私有变量

    函数内部定义的变量,外部不能访问,但是闭包可以通过作用链来访问这些变量。

(function() {
    var name = "";
    Person = function(value) {
        name = value;
    }
    
    Person.prototype.getName = function(){
        return name;
    }
    
    Person.prototype.setName = function(){
        name = value;
    }
};)();

name变成一个静态的、由所有实例共享的属性。改变其中的name,会使得每个实例的值都一样。
在一个实例上调用setName()会影响所有实例,而调用setName() 或 新建一个Person实例都会赋予name属性一个新值。

  • 模块模式 module pattern
    为单例添加私有变量和特权方法。
    如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些似有数据的方法,那么就可以使用模块模式了。

Chapter 8   BOM

BOM的核心对象是window,它表示浏览器的一个实例。

  • 全局作用域

所有全局作用域中声明的变量、函数都会变成window对象的属性和方法。

var age = 19;
window.color="cc"

delete age;     //false,  IE<9时抛出错误
delete window.color;    //true, IE<9抛出错误

因为var语句自动定义了一个configurable的属性值为false,不能通过delete操作符删除。

  • 窗口位置
var leftPos = (typeof window.screenLeft == "number") ? screenLeft : window.screenX;
这事先判断浏览器是否支持screeLeft,如果不支持泽调用screenX/Y
moveTo(x,y): 将窗口移动到具体坐标值,左上角为(0,0)

moveBy(x,y): 接受的参数为水平和垂直方向上的像素值。

window.moveTo(0,0);

  • 导航和弹出窗口
window.open(url, "target", "atrribute", "是否取代浏览器历史记录中当前大家页面/true or false"); 

具有四个参数, 第四个参数只在不打开新窗口的时候使用

var blocked = false;
try{
    var win = window.open("http://www.baidu.com");
    if (win == null) {
        blocked = true;
    }

} catch(ex) {
    blocked = true;
}

    在任何情况下,以上代码都可以检测出调用window.open() 打开的弹出窗口是不是被屏蔽了。

  • 间歇调用和超时调用
var timeout = setTimeout(function(){
    alert("a");
}, 1000);

第一个参数:传递字符串可能导致性能损失,所以不建议

第二个参数:JavaScript再过多长时间把当前任务添加到队列中。如果队列是空的,添加的代码会立即执行,如果不是空的,它要等前面的代码执行完了以后再执行。

取消:只要是在指定的时间尚未过去之前调用clearTimeout() ,就可以完全取消超时调用

var timeout = setInteval(function(){
    alert("a");
}, 1000);

  • 系统对话框

confirm()会返回一个boolean值。

if(confirm("nice?")) {
    alert(yes);
} else {
    alert(no);
}

  • navigator对象

主要用于检测显示网页的浏览器类型。

  • history对象

    history对象上保存着用户上网的历史记录。但是开发人员无法得知用户浏览过的URL。
    使用go()方法可以在用户的历史记录中任意跳转。

history.go(1); 前进一页
history.go(-1); 后退一页
history.go(2); 前进两页

Chapter 10   DOM

。。。

Chapter 13    事件 

JavaScript 与 HTML之间的交互式通过事件实现的。事件就是文档或者浏览器窗口中发生的一些特定的交互瞬间。
  • 事件流
事件流描述的是从页面接受事件的顺序。
  • 事件冒泡 event bubbling
事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点(文档)。
  • 事件捕获 event capturing
不太具体的几点应该更早接收到事件,而最具体的节点应该最后接收到事件。 事件捕获的用意在于再事件到达预定目标之前捕获它。
  • DOM事件流
DOM2级事件规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。
  1. 捕获阶段到目标元素的父节点就停止了。
  2. 处于目标阶段,事件在目标上发生。
  3. 在事件冒泡阶段可以对事件做出响应。 高版本的浏览器会在捕获阶段出发事件对象上的事件。
  • DOM0级事件处理程序
将一个函数赋值给一个事件处理程序属性。
var btn = document.getElementById("myBtn");
myBtn.onclick = function() {
    alert("clicked")
 }
删除事件处理程序
btn.onclick = null;
  • DOM2  级事件处理程序
DOM2级定义了两个方法:addEventListener()、removeEventListener()
        接受3个参数:要处理的事件名(没有on),事件处理函数,boolean参数(true:在捕获阶段调用函数,false:在冒泡阶段调用函数)
btn.addEventListener("click", function(){}, false);
        大多数情况下,将事件处理程序添加到事件流的冒泡阶段,可以兼容各种浏览器。 DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
  • IE事件处理程序
IE中的两个方法:attachEvent()、detachEvent()
        都接收两个参数:事件处理程序名称, 事件处理函数。
var btn = document.getElementById("myBtn");
myBtn.attachEvent("onclick", function(){});
IE中的监听器与DOM0级的主要区别在于事件处理程序的作用域:
        DOM0级事件处理程序会在其所属元素的作用域内运行
        IE的则会在全局作用域中运行。
 IE中监听器与DOM2级的相同与区别:
共同点
        都可以为一个元素添加多个事件处理程序
区别
        DOM2级在一个元素多个事件处理程序的情况下,是按照书写顺序执行事件的。
        IE中则是从最后的开始处理,以相反的顺序被触发。
  • 跨浏览器的事件处理程序
var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {        
            element.addEventListener(type, handler, false);    //DOM2 级方法
        } else if (element.attachEvent) {
            element.attachEvent("on"+type, handler);        //IE 方法
        } else {
            element["on" + type] = handler;        //DOM0 级方法
        }
    }

    removeHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.detachEvent("on"+type, handler);
        } else {
            element["on" + type] = null;
        }
    }
}






猜你喜欢

转载自blog.csdn.net/weixin_41892205/article/details/80355437