JavaScript 入门

1. 概述

1.2 语言特性

1.2.1 动态性

:只需在使用时做赋值操作即可

var obj = new Object();
obj.name = "an object";
obj.sayHi = function() {
    return "Hi";
}
obj.sayHi();

/*
输出结果:
< 'Hi'
*/

当不需要一个对象的属性时,

obj.name
/*
输出结果:
< 'an object'
*/

delete obj.name
/*
输出结果:
< true
*/

obj.name
/*
undefined
*/

另外一点,可以动态访问一个 JavaScript 对象的属性。

var key = "property";
console.log(key);
/*
输出结果:
< property
*/

var obj = {
    property: "my property"
}
console.log(obj[key]);
/*
输出结果:
< my property
*/
1.2.2 弱类型

:数据类型无需在声明时指定,解释器会根据上下文对变量进行实例化。

var s = "text";
console.log(s);
/*
输出结果:
< text
*/

s = 12 + 5;
console.log(s);
/*
输出结果:
< 17
*/

s = 6.3;
console.log(s);
/*
输出结果:
< 6.3
*/

s = new Object();
s.name = "object";
console.log(s.name);
/*
输出结果:
< object
*/
1.2.3 面向对象

代码本身可以作为参数传递给其他的代码。

var array = [1, 2, 3, 4, 5];
array.map(function(item){
    return item * 2;
});
/*
输出结果:
< [2, 4, 6, 8, 10]
*/

map 函数可以处理更复杂的场景,

var staff = [
    {name: 'abruzzi', age: 24},
    {name: 'bajmine', age: 26},
    {name: 'chris', age: 25}
];
staff.map(function(item){
    return item.name.toUpperCase();
});
/*
输出结果:
< ['ABRUZZI', 'BAJMINE', 'CHRIS']
*/

与 map 函数类似,filter 函数用于过滤数组中满足某些条件的元素,

var staff = [
    {name: 'abruzzi', age: 24},
    {name: 'bajmine', age: 26},
    {name: 'chris', age: 25}
];
staff.filter(function(item){
    return item.age > 24;
});
/*
输出结果:
< [
	{name: 'bajmine', age: 26},
	{name: 'chris', age: 25}
  ]
*/

1.3 应用范围

1.3.2 服务器端 JavaScript

node.js 的例子,

//ERROR
var sys = require('sys'),
    http = require('http');
http.createServer(function(req, res){
    setTimeout(function(){
        res.sendHeader(200,{'Content-Type': 'text/plain'});
        res.sendBody('Hello World');
        res.finish();
    },2000);
}).listen(8000);
sys.puts('Server running at http://127.0.0.1:8000/');

例子二,

//ERROR
var tcp = require('tcp');
var server = tcp.createServer(function(socket){
    socket.setEncoding("utf-8");
    socket.addListener("contect",function(){
        socket.send("hello\r\n");
    });
    socket.addListener("receive",function(data){
        socket.send(data);
    });
    socket.addListener("eof",function(){
        socket.send("goodbye\r\n");
    	socket.close();
    });
});
server.listen(7000,"localhost");

2. 基本概念

2.1 数据类型

2.1.1 数据类型

JavaScript 包含 6 种数据类型:字符串(string) 、数值(number)、布尔值(boolean)、未定义(undefined)、不存在(null)、对象(object)。

var str = "Hello";
var i = 10;
var f = 2.3;
var b = true;

console.log(str);
console.log(i);
console.log(f);
console.log(b);

console.warn(typeof str);
console.warn(typeof i);
console.warn(typeof f);
console.warn(typeof b);
console.warn(typeof x);
2.1.2 对象类型

对象类型是一种复合的数据类型,其基本元素有基本数据类型组成。

var str = "Hello";
var obj = new Object();
obj.str = str;
obj.num = 2.3;

var array = new Array("foo","bar","zoo");

var func = function(){
    console.log("A function");
}

console.log(typeof obj);	//> object
console.log(typeof array);	//> object
console.log(typeof func);	//> function
2.1.3 基本类型与对象间的转换

基本类型转换为对象类型的实例,

var str1 = "JavaScript Kernal";
console.log(str1.length);		//> 17
console.log(typeof str1);		//> string

var str2 = new String("JavaScript Kernal");
console.log(str2.length);		//> 17
console.log(typeof str2);		//> object

此时 str1 作为 string,与 object 完全不同,却都可以调用函数 str.length,说明在使用该函数时,JavaScript 会自动包装一个临时的 Sring 对象,内容为 str1 的内容,然后获取该对象的 length 属性,最后将这个临时对象释放。

而将对象转换为基本类型的方式为:通过调用对象的 valueOf() 方法来取得对象的值。如果取不到值,则需调用对象的 toString() 方法,必要时将该字符串值转换为数值。事实上这种转换存在许多问题,例如,

function convertTest(){
    if(new Boolean(false) && new Object() && new String("") && new Array()){
        console.log("convert to boolean")
    }
}
convertTest();		//> convert to boolean

例子二,

var x = 3;
var y = x + "2";
var z = x + 2;
console.log(y);		//> 32
console.log(z);		//> 5
2.1.4 类型的判断

一般情况下,typeof 可以胜任大部分的类型判断工作,

function handleMessage(message, handle){
    if(typeof handle == "function"){
        return handle(message);
    }else{
        throw new Error("the 2nd argument should be a function");
    }
}

但在以下情况中就会出现问题,

var obj = {};
var array = ["one","two"];
console.log(obj);		//> object
console.log(array);		//> object

此时,可以利用 instanceof 来进行下一步的判断,

var obj = {};
var array = ["one","two"];
console.log(obj instanceof Array);		//> false
console.log(array instanceof Array);	//> true

2.2 变量

2.2.1 基本类型和引用类型

区别:通过变量直接访问基本类型的数据;通过对其引用的访问来访问引用类型的本身。

//基本类型
var x = 1;
var y = x;
console.log(x + " " + y);	//> 1 1
x = 2;
console.log(x + " " + y);	//> 2 1
//引用类型
var array = [1, 2, 3, 4, 5];
var arrayRef = array;

array.push(6);
console.log(arrayRef);		//> [1, 2, 3, 4, 5, 6]
2.2.2 变量的作用域

声明在函数内部的变量具有局部作用域,在函数的外部不可访问,

var variable = "out";
function func(){
    var variable = "in";
    console.log(variable);	//> in
}
func();
console.log(variable);		//> out

同时,在局部默认的操作会影响全局变量,

var variable = "out";
function func(){
    variable = "in";
    console.log(variable);	//> in
}
func();
console.log(variable);		//> in

2.3 运算符

2.3.1 中括号运算符

(一)数组对象,从数组中按下标取值。

var array = ["one", "two", "three", "four"];
array[0];

(二)对象

var object = {
    field : "self",
    printInfo : function(){
        console.log(this.field);
    }
}
object.field;
object.printInfo();		//> self

for (var key in object){
    console.log(key + " : " + object[key]);
    				/*
    					*> field : self
    					*> printInfo : function(){
                                    console.log(this.field);
                                }
    				*/
}
2.3.2 点运算符

对象 . (属性 / 对象)

var object = {
    field : "self",
    printInfo : function(){
        console.log(this.field);
    },
        outter:{
            inner : "inner text",
                printInnerText : function(){
                    console.log(this.inner);
                }
        }
}
object.outter.printInnerText();		//> inner text

上述 Code 中,outter 既是 object 的属性,也是 printInnerText 的对象。

如果一个对象的属性本身就包含点的键(self.ref),此时点运算符就无用了。

var ref = {
    id : "rerference1",
    func : function() {
        return this.id;
    }
};
var obj = {
    id : "object1",
    "self.ref" : ref
};
console.log(obj["self.ref"].func());	//> reference1
2.3.3 相等于等同运算符

相等:==

等同:===

console.log(1 == true);		//> true
console.log(1 === true);	//> fasle
console.log("" == false);	//> true
console.log("" === false);	//> false
console.log(null == undefined);  //> true
console.log(null === undefined); //> false

比如:

var obj = {
    id : "self",
    name : "object"
};
var oa = obj;
var ob = obj;
console.log(oa == ob);		//> true
console.log(oa === ob);		//> true
var obj1 = {
    id : "self",
    name : "object",
    toString : function(){
        return "object 1";
    }
}
var obj2 = "object 1";
console.log(obj1 == obj2);		//> true
console.log(obj1 === obj2);		//> false

3. 对象

3.1 JavaScript 对象

3.1.1 对象的属性
//声明一个对象
var jack = new Object();
jack.name = "jack";
jack.age = 26;
jack.birthday = new Date(1984, 4, 5);

//声明另一个对象
var address = new Object();
address.street = "HQ Road";
address.xno = "135";

jack.addr = address;
console.log(jack);


var ja = jack.addr;
ja = jack["addr"];
console.log(ja);
3.1.2 属性与变量

JavaScript 引擎在初始化时,会构建一个全局对象;在客户端环境中,该全局对象即为 window。

如果在其他的 JavaScript 环境中需要引用这个全局对象,只需在顶级作用域(即所有函数声明之外的作用域)声明即可。

在客户端中,以下 Code

var v = "global";
var array = ["hello", "world"];
function func(id){
    var element = document.getElementById(id);
}

事实上等于,

window.v = "global";
window.array = ["hello", "world"];
window.func = function(id){
    var element = document.getElementById(id);
}

由此,变量就是全局对象(global)的属性。

3.1.3 原型对象及原型链

原型(prototype)是 JavaScript 特有的概念。通过使用原型,JavaScript 可以建立其对象面向对象语言中的继承。

JavaScript 本身是基于原型的,每个对象都有一个 prototype 属性,这个 prototype 本身也是一个对象,因此也有自己的原型,从而构成了一个链结构。

举例,

function Base(name){
    this.name = name;
    this.getName = function(){
        return this.name;
    }
}
function Child(id){
    this.id = id;
    this.getId = function(){
        return this.id;
    }
}
Child.prototype = new Base("base");
var c1 = new Child("child");
console.log(c1.getId());	//> child
console.log(c1.getName());	//> base

由于遍历原型链时,是由下而上的,所以最先遇到的属性值最先返回。

function Person(name, age){
    this.name = name;
    this.age = age;
    this.getName = function(){
        return this.name;
    }
    this.getAge = function(){
        return this.age;
    }
}
var tom = new Person("Tom", 38);
var jerry = new Person("Jerry", 6);

console.log(tom);
console.log(jerry.getName()+" "+jerry.getAge());	//> Jerry 6
3.1.4 this 指针

this 表示当前上下文(context),即对调用者的引用。

举例,

var jack = {
    name : "jack",
    age : 26
}
var abruzzi = {
    name : "abruzzi",
    age : 26
}
function showName(){
    return this.name;
}
console.log(showName.call(jack));		//> jack
console.log(showName.call(abruzzi));	//> abruzzi

3.2 使用对象

定义对象的 “类”:原型,然后使用 new 操作符来批量构建新的对象。

function Address(street, xno){
    this.street = street || 'HQ Road';
    this.xno = xno || 135;
    this.toString = function(){
        return "street: " + this.street + ", No: " + this.xno;
    }
}
function Person(name, age, addr){
    this.name = name || 'unknown';
    this.age = age;
    this.addr = addr || new Address(null, null);
    this.getName = function() { return this.name; }
    this.getAge = function() { return this.age; }
    this.getAddr = function() { return this.addr.toString(); }
}

var jack = new Person('jack', 26, new Address('QH Road', 123));
var abruzzi = new Person('abruzzi', 26);

console.log(jack.getName());	//> jack
console.log(jack.getAge());		//> 26
console.log(jack.getAddr());	//> street: QH Road, No: 123

console.log(abruzzi.getName());	//> abruzzi
console.log(abruzzi.getAge());	//> 26
console.log(abruzzi.getAddr());	//> street: HQ Road, No: 135

3.3 对象字面量

例如,

var obj = {
    name : "abruzzi",
    addr : {
        street : "HQ Road",
        xno : "135"
    }
}

对于一个函数有多个返回值时,

function point(left, top){
    this.left = left;
    this.top = top;
    return { x: this.left, y: this.top };
}
var pos = point(3, 4);
//pos.x = 3;
//pos.y = 4;

for...in 语法糖,

for(var item in json){
    //item 为键
    //json[item] 为值
}

在实际 Web 应用中,

var style = {
    border : "1px solid #ccc",
    color : "blue"
}

//添加 DOM 元素动态地添加这些属性:
for(var item in style){
    //使用 jQuery 的选择器
    $("div#element").css(item, style[item]);
}

另外,当需要用户自定义设置时,用户将需要设置的内容填入这个对象,

function customize(options){
    this.default = {
        width : 100,
        height : 100,
        background : '#ccc'
    };
    //jQuery 中的 extend 方法可以将两个对象的内容合并
    this.setting = $.extend(this.default, options);
}
constomize({
    background: '#6b6b6b'
});
this.setting = {
    width : 100,
    height : 100,
    background : '#6b6b6b'
};

3.4 JSON

:前端与服务器端的数据交换的标准格式:

​ 前端程序通过 Ajax 发送 JSON 到后端,服务器端脚本对 JSON 进行解析,还原成服务器端对象,经过处理,返回给前端的仍然是 JSON 数据。JSON 相对 XML 更高效,冗余更小。

一个典型的 JSON 格式的数据看起来是这样的:

{
    "content":"make the index page more fancy of feather",
    "complete":false
}

客户端组织这样的 JSON,通过 HTTP 发送给服务器端,服务器根据 JSON 解析器将其转换为服务器端对应的模型对象(如 Java 对象 / Ruby 对象),再进行数据库访问,创建完成后将结果转换为 JSON 格式返回给客户端。

{
    "id": 1,
    "content": "make the index page more fancy of feather",
    "complete": false,
    "created_at": "2012-01-24TH16:03:13+08:00",
    "updated_at": "2012-01-24TH16:03:13+08:00",
    "user_id": 1
}

4. 函数

4.1 函数对象

4.1.1 创建函数

通过字面量来创建函数,

function add(x, y){
    return x + y;
}
//或
var add = function(x, y){
    return x + y;
}

函数作为一个独立的对象而存在于 JavaScript 的运行系统,例如,

function p(){
    console.log("invoke p by()");
}
p.id = "func";
p.type = "function";
console.log(p);		/*> p(){
    					 console.log("invoke p by()");
					   }
				   */
console.log(p.id + ":" + p.type);	//> func:function
console.log(p());	//> invoke p by()
4.1.2 函数的参数

任意多的参数传递给一个函数,比如,

function adPrint(str, len, option){
    var s = str || "default";
    var l = len || s.length;
    var o = option || "i";
    s = s.substring(0, l);
    switch(o){
        case "u":
            s = s.toUpperCase();
            break;
        case "l":
            s = s.toLowerCase();
            break;
        default:
            break;
    }
    console.log(s);
}

adPrint("Hello, world");		    //> Hello world
adPrint("Hello, world", 5);		    //> Hello
adPrint("Hello, world", 5, "l");	//> hello
adPrint("Hello, world", 5, "u");	//> HELLO

arguments 的例子,

function sum(){
    var result = 0;
    for(var i = 0, len = arguments.length; i < len; i++){
        var current = arguments[i];
        if(isNaN(current)){
            throw new Error("not a number exception");
        }else{
            result += current;
        }
    }
    return result;
}
console.log(sum(10, 20, 30, 40, 50));	//> 150
console.log(sum(4, 8, 15, 16, 23, 42));	//> 108
console.log(sum("new"));			//> Error: not a number exception

4.2 函数作用域

4.2.1 词法作用域

JavaScript 中的变量作用域为函数体内有效,而无块作用域。

在 Java 中,

public void method(){
    for(int i = 0; i < obj1.length; i++){
        //do sth here
    }
    for(int i = 0; i < array.length; i++){
        //do sth here
    }
}

而在 JavaScript 中,

function func(){
    for(var i = 0; i < array.length; i++){
        //do sth here
    }
    console.log(i);
}

局部作用域内运行的函数体可以访问其外层的变量与函数,例如,

var str = "global";
function scopeTest(){
    console.log(str);	//> undefined
    var str = "local";	//> local
    console.log(str);
}
scopeTest();
4.2.2 调用对象

:在一个函数中,作用域链上会有两个对象,第一个(首先被访问到的)为调用对象(又称活动对象),第二个为全局对象。

4.3 函数上下文

4.4 call 和 apply

:call 和 apply 通常用来修改函数上下文。

举例,

var jack = {
    name : "jack",
    age : 26
}
var abruzzi = {
    name : "abruzzi",
    age : 26
}
function printName(){
    return this.name;
}
//设置 printName 的上下文为 jack,此时的 this 为 jack
console.log(printName.call(jack));		//> jack
//设置 printName 的上下文为 abruzzi,此时的 this 为 abruzzi
console.log(printName.call(abruzzi));	//> abruzzi

console.log(printName.apply(jack));		//> jack
console.log(printName.apply(abruzzi));	//> abruzzi

只有一个参数时 call 与 apply 的使用方式相同,当有多个参数时,

setName.apply(jack, ["Jack Sept."]);
console.log(printName.apply(jack));
setName.call(abruzzi, "John Abruzzi");
console.log(printName.call(abruzzi));

4.5 使用函数

4.5.1 赋值给一个变量
function add(x, y){
    return x + y;
}
var a = 0;
a = add;
var b = a(2, 3);
console.log(b);	//> 5
4.5.2 赋值为对象的属性
var obj = {
    id : "obj1"
}
obj.func = add;
obj.func(2, 3);	//> 5
4.5.3作为参数传递
function adPrint2(str, handler){
    console.log(handler(str));
}
function up(str){
    return str.toUpperCase();
}
function low(str){
    return str.toLowerCase();
}
adPrint2("Hello, world", up);	//> HELLO,WORLD
adPrint2("Hello, world", low);	//> hello, world
4.5.4 作为函数的返回值
function currying(){
    return function(){
        console.log("curring");
    }
}
currying()();	//> curring

5. 数组

数组对象的方法

方法 描述
concat() 连接连个以上的数组,并返回结果数组
join() 把数组的所有元素放入一个字符串
pop() 删除并返回数组的最后一个元素
push() 向数组的末尾添加元素,并返回新的长度
reverse() 翻转数组中的元素顺序
shift() 删除并返回数组的第一个元素
slice() 从某个已有的数组返回选定的元素
sort() 对数组中的元素进行排序
splice() 删除元素,并向数组添加新元素
unshift() 向数组的开头添加元素,并返回新的长度
valueOf() 返回数组对象的原始值

5.1 数组的特性

(一)length 这个变量并非只读属性,

var array = new Array(1, 2, 3, 4, 5);
console.log(array.length);	//> 5
array.length = 3;
console.log(array.length);	//> 3
console.log(array);			//> [1, 2, 3]

(二)字符串也可以作为数组的下标

var stack = new Array();
stack['first'] = 3.1415926;
stack['second'] = "okay then.";
stack['third'] = new Date();
for(var item in stack){
    console.log(typeof stack[item]);
}
/*
输出结果:
number
string
object
*/

5.2 使用数组

5.2.1 数组的基本方法使用
//创建数组
var array = [];

//向数组中添加元素
array.push(1);
array.push(2);
array.push(3);
array.push("four");
array.push("five");
array.push(3.1415926);

//弹出数组中的元素
var len = array.length;
for(var i = 0; i < len; i++){
    console.log(typeof array[i] + ": " + array.pop());
}
console.log("Length: " + array.length);		//> Length: 0

//join,连接数组元素为一个字符串
array = ["one", "two", "three", "four", "five"];
var str1 = array.join(",");
var str2 = array.join("|");
console.log(str1);	//> one,two,three,four,five
console.log(str2);	//> one|two|three|four|five

//连接多个数组为一个数组
var another1 = ["this", "is", "another", "array"];
var another2 = ["yet", "another", "array"];
var bigArray = array.concat(another1, another2);
console.log(bigArray.join(","));
//> one,two,three,four,five,this,is,another,array,yet,another,array

//取出一定数量的元素
console.log(bigArray.slice(5,9));	//> ['this', 'is', 'another', 'array']

//splice 方法
bigArray.splice(5, 2);	//自第 5 个元素起,删除 2 个元素
console.log(bigArray);
//自第 5 个元素起,删除 0 个元素,并在第 5 个元素后插入新的参数
bigArray.splice(5, 0, "very", "new", "item", "here");
console.log(bigArray);

//sort 方法对数组进行排序
var arraySort = ["Cisio", "Borland", "Apple", "Dell"];
console.log(arraySort);	//> ['Cisio', 'Borland', 'Apple', 'Dell']
arraySort.sort();
console.log(arraySort);	//> ['Apple', 'Borland', 'Cisio', 'Dell']

arraySort = [11, 222, 3333, 4444];
arraySort.sort(function(a, b){ return a-b; })	//正序
console.log(arraySort);		//> [11, 222, 3333, 4444]
arraySort.sort(function(a, b){ return b-a; })	//逆序
console.log(arraySort);		//> [4444, 3333, 222, 11]
5.2.2 删除数组元素

delete 方法可以删除元素的内容,

var array = ["one", "two", "three", "four"];
console.log(array);	//> ['one', 'two', 'three', 'four']
delete array[2];
console.log(array);	//> ['one', 'two', empty, 'four']

利用 jQuery 中的 remove 方法可以删除数组的元素,

var array = ["one", "two", "three", "four", "five", "six"];
console.log(array);
array.remove(0);	//删除第一个元素
console.log(array);
array.remove(-1);	//删除最后一个元素
console.log(array);
array.remove(0, 2);	//删除数组中下标为 0~2 的元素
console.log(array);
5.2.3 遍历数组
var array = [1, 2, 3, 4];
for(var item in array){
    console.log(array[item]);
}

但 for...in 方法不总是有效,例如,

Array.prototype.useless = function(){}

这时,应考虑使用 for 循环,例如,

var array = [1, 2, 3];
for(var i = 0; i < array.length; i++){
    console.log(array[i]);
}

6. 正则表达式

6.1 基础概念

6.1.1 元字符与特殊字符
元字符 含义
^ 串的开始
$ 串的结束
* 零到多次匹配
+ 一到多次匹配
? 零或一次匹配
\b 单词边界
字符 含义
字符本身 匹配字符本身
\r 匹配回车
\n 匹配换行
\t 制表符
\f 换页
\x# 匹配十六进制数
\cX 匹配控制字符
6.1.2 范围及重复
标识符 含义
[...] 在集合中的任一个字符
[^...] 不在集合中的任一个字符
. 除\n之外的任一个字符
\w 所有的单字,包括字母,数字及下划线
\W 不包括所有的单字
\s 所有的空白字符,包括空格与制表符
\S 所有的非空白字符
\d 所有的数字
\D 所有的非数字
\b 退格字符
var emailval = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
emailval.test("abcd@gmail.com");				//> true
emailval.test("john.abruzzi@pl.kunming.china");	  //> true
emailval.test("@invalid.com");					//> false

对 C/C++/Java 语言中的变量命名检查,

var variable = /^[a-zA-z_][a-zA-z0-9_]*$/;
console.log(variable.test("hello"));	//> true
console.log(variable.test("g00le"));	//> true
console.log(variable.test("_abcd"));	//> true
console.log(variable.test("o8179"));	//> true
console.log(variable.test("08179"));	//> false
console.log(variable.test("8_h_w"));	//> false
console.log(variable.test("@_h_w"));	//> false

正则表达式的重复

标记 含义
重复 n 次
重复 n 或更多次
重复至少 n 次,m 次
var pid = /^[\d{15}|\d{18}]$/;	//身份证号码
var mphone = /\d{11}/;			//手机号码
var phone = /\d{3,4}-\d{7,8}/;	//座机号码
console.log(pid.test("210211200001015623"));	//> false ???
console.log(mphone.test("13545689000"));		//>	true
console.log(phone.test("010-99392333"));		//> true
console.log(phone.test("0791-88650279"));		//> true
6.1.3 分组与引用

(一)括号用来将子表达式标记起来,以区别于其他表达式,如,

h(elp)?	//字符 h 之后的 elp 可有可无

(二)分组,如,

var pattern = /\w{4}(\d{4})(\w{2})/;
var result = pattern.exec("yunn0871cg");
console.log("city code = " + result[1] + ", county code = " + result[2]);
//> city code = 0871, county code = cg
result = pattern.exec("shax0917cc");
console.log("city code = " + result[1] + ", county code = " + result[2]);
//> city code = 0917, county code = cc

(三)对引用起辅助作用,如,...

6.2 使用正则表达式

6.2.1 创建正则表达式

使用字面量,

var regex = /pattern/;

使用 RegExp 对象,

var regex = new RegExp("pattern", switchs);

正则表达式的一般形式描述为,

var regex = /pattern/[switchs];

这里的开关(switchs)有以下三种,

修饰符 描述 举例
i 忽略大小写开关 /java/i可以匹配java/Java/JAVA
g 全局搜索开关 /java/g匹配"javascript&java"中的两个"java"
m 多行搜索开关(重定义 ^ 与 $ 的意义) Code 如下
var pattern = /^javascript/;
console.log(pattern.test("java\njavascript"));	//> false
pattern = /^javascript/m;
console.log(pattern.test("java\njavascript"));	//> true

RegExp 对象的方法,

方法名 描述 使用方式
test() 测试串中是否有合乎模式的匹配 测试
exec() 对串进行匹配 分组
cpmpile() 编译正则表达式 改变表达式模式
6.2.2 String 中的正则表达式

String 对象中的正则表达式

方法 作用
match 匹配正则表达式,返回匹配数组
replace 替换
split 分割
search 查找,返回首次发现的位置

match 举例,

var str = "life is very much like a mirror.";
var result = str.match(/is|a/g);
console.log(result);	//> ['is', 'a']

replace 举例,

var str = "<span>Welcome</span>";
var res = str.replace(/span/g, "div");
console.log(str);	//> <span>Welcome</span>
console.log(res);	//> <div>Welcome</div>
var str = "<span>abc, bcd</span>";
var res = str.replace(/(\w+),\s(\w+)/g, "$2, $1");
console.log(str);	//> <span>abc, bcd</span>
console.log(res);	//> <span>bcd, abc</span>

split 举例,

var str = "john : tomorrow		:remove:file";
var res = str.split(/\s*:\s*/);
console.log(str);	//> john : tomorrow		:remove:file
console.log(res);	//> ['john', 'tomorrow', 'remove', 'file']

search 举例,

var str = "Tomorrow is another day";
var index = str.search(/another/);
console.log(index);	//> 12

7. 闭包

7.1 闭包的特性

var outter = [];
function clouseTest(){
    var array = ["one", "two", "three", "four"];
    for(var i = 0; i < array.length; i++){
        var x = {};
        x.no = i;
        x.text = array[i];
        x.invoke = function(){
            console.log(i);
        }
        outter.push(x);
    }
}
clouseTest();
console.log(outter[0].invoke());	//> 4
console.log(outter[1].invoke());	//> 4
console.log(outter[2].invoke());	//> 4
console.log(outter[3].invoke());	//> 4

理论上,输出内容应为 0 1 2 3

实际上输出内容为 4 4 4 4

为解决上述问题,可声明一个匿名函数,Code 如下,

var outter = [];
function clouseTest2() {
    var array = ["one", "two", "three", "four"];
    for(var i = 0; i < array.length; i++){
        var x = {};
        x.no = i;
        x.text = array[i];
        x.invoke = function(no){
            return function(){
                console.log(no);
            }
        }(i);
        outter.push(x);
    }
}
clouseTest2();
console.log(outter[0].invoke());	//> 0
console.log(outter[1].invoke());	//> 1
console.log(outter[2].invoke());	//> 2
console.log(outter[3].invoke());	//> 3

7.2 闭包的用途

7.2.1 匿名自执行函数...
var datamodel = {
    table : {},
    tree : {}
};
(function(dm){
    for(var i = 0; i < dm.table.rows; i++){
        var row = dm.table.rows[i];
        for(var j = 0; j < row.cells; i++){
            drawCell(i, j);
        }
    }
    //build dm.tree
})(datamodel);
7.2.2 缓存...
var CacheSearchBox = (function(){
    var cache = {},
        count = [];
    return {
        attachSearchBox : function(dsid){
            if(dsid in cache){
                return cache[dsid];
            }
            var fsb = new uikit.webctrl.SearchBox(dsid);
            cache[dsid] = fsb;
            if(count.length > 100){
                delete cache[count.shift()];
            }
            return fsb;
        },
        clearSearchBox : function(dsid){
            if(dsid in cache){
                cache[dsid].clearSelection();
            }
        }
    };
})();
CachedSearchBox.attachSearchBox("input1");
7.2.3 实现封装

(一)在 person 之外的地方无法访问其内部变量,可以提供闭包的形式来访问。

var person = function(){
    var name = "default";
    return {
        getName : function(){
            return name;
        },
        setName : function(newName){
            name = newName;
        }
    }
}();
console.log(person.name);		//> undefined
console.log(person.getName());	//> default
person.setName("abruzzi");
console.log(person.getName());	//> abruzzi

(二)实现面向对象中的对象。

function Person(){
    var name = "default";
    return {
        getName : function(){
            return name;
        },
        setName : function(newName){
            name = newName;
        }
    }
};
var john = Person();
console.log(john.getName());	//> default
john.setName("john");
console.log(john.getName());	//> john

var jack = Person();
console.log(jack.getName());	//> default
jack.setName("jack");
console.log(jack.getName());	//> jack

7.面向对象的 JavaScript

7.1 原型继承

JavaScript 的继承可以通过原型链来实现,调用对象的上一个方法。

举例,

//ERROR
function Base(){
    this.baseFunc = function(){
        console.log("base behavior");
    }
}
function Middle(){
    this.middleFunc = function(){
        console.log("middle behavior");
    }
}
Middle.prototype = new Base();
function Final(){
    this.finalFunc = function(){
        console.log("final behavior");
    }
}
Final.prototype = new Middle();
function test(){
    var obj = new Final();
    obj.baseFunc();
    obj.middleFunc();
    obj.finalFunc();
}
7.1.1 引用

JavaScript 中的引用始终指向最终的对象,并非引用本身,举例,

var obj = {};
var ref = obj;

obj.name = "objectA";
console.log(ref.name);		//> objectA

obj = ["one", "two", "three", "four"];
console.log(ref.name);		//> objectA
console.log(obj.length);	//> 4
console.log(ref.length);	//> undefined

在定义了引用后,修改原始的那个对象会影响到其引用上,举例,

var obj = {};
var ref1 = obj;
var ref2 = obj;
obj.func = "function";
console.log(ref1.func);	//> function
console.log(ref2.func);	//> function
7.1.2 new 操作符

纠正 Java 程序员对 new 操作符的固有认知,举例,

function Shape(type){
    this.type = type || "rect";
    this.calc = function(){
        return "calc, " + this.type;
    }
}
var triangle = new Shape("triangle");
console.log(triangle.calc());	//> calc, triangle
var circle = new Shape("circle");
console.log(circle.calc());		//> calc, circle

在 JavaScript 中,通过 new 操作符来作用于一个函数,举例,

function Shape(type){
    this.type = type || "rect";
    this.calc = function(){
        return "calc, " + this.type;
    }
}
var triangle = new Shape("triangle");
var triangle = {};
Shape.apply(triangle, ["triangle"]);

7.2 封装

封装的好处在于未经授权的客户代码无法访问到我们不公开的数据,举例,

function Person(name){
    var address = "The Earth";
    this.getAddress = function(){
        return address;
    }
    this.name = name;
}
Person.prototype.getName = function(){
    return this.name;
}
Person.prototype.setName = function(name){
    this.name = name;
}
var jack = new Person("jack");
console.log(jack.name);			//> jack
console.log(jack.getName());	//> jack

console.log(jack.address);		//> undefined
console.log(jack.getAddress());	//> The Earth

也可以为类添加静态成员,比如,

function Person(name){
    var address = "The Earth";
    this.getAddress = function(){
        return address;
    }
    this.name = name;
}
Person.TAG = "javascript-core";
console.log(Person.TAG);	//> javascript-core

7.3 工具包 Base

假设我们在开发一个任务系统,需要抽象出一个类来表示任务,对应的,每个任务都可能会有一个监听器,Code 如下,

//ERROR
//首先定义一个事件监听类
var EventListener = Base.extend({
    constructor : function(sense){
        this.sense = sense;
    },
    sense : null,
    handle : function(){
        console.log(this.sense + " occured");
    }
});

//然后定义一个任务类
var Task = Base.extend({
    constructor : function(name){
        this.name = name;
    },
    name : null,
    listener : null,
    execute : function(){
        console.log(this.name);
        this.listener.handle();
    },
    setListener : function(listener){
        this.listener = listener;
    }
});

var printing = new Task("printing");
var printEventListener = new EventListener("printing");
printing.setListener(printEventListener);
printing.execute();

var HttpRequester = Task.extend({
    constructor : function(name, host, port){
        this.base(name);
        this.host = host;
        this.port = port;
    },
    host : "127.0.0.1",
    port : 9527,
    execute : function(){
    	console.log("[" + this.name + "] request send to " + this.host + " of port " +this.port);
    	this.listener.handle();
	}
});

var requester = new HttpRequester("requester1", "127.0.0.1", 8752);
var listener = new EventListener("http_request");
requester.setListener(listener);
requester.execute();

8. 函数式的 JavaScript

关于函数可以保持自己内部的数据这一特性,称为闭包。举例,

var outter = function(){
    var x = 0;
    return function(){
        return x++;
    }
}
var a = outter();
console.log(a());
console.log(a());	//> 0
var b = outter();	//> 1
console.log(b());	//> 0
console.log(b());	//> 1

8.1 匿名函数

:函数不被赋予名字

function func(){
    //do something
}
var func = function(){
    //do something
}

8.2 高阶函数

:以一个或多个函数为参数的函数称为高阶函数。

8.2.1 JavaScript 中的高阶函数...
Array.prototype.map = function(func /*, obj */){
    var len = this.length;
    if(typeof func != "function"){
        throw new Error("argument should be a function!");
    }
    var res = [];
    var obj = argument[1];
    for(var i = 0; i < len; i++){
        res[i] = func.call(obj, this[i], i, this);
    }
    return res;
}

function double(x){
    return x * 2;
}
[1, 2, 3, 4, 5].map(double);

[
    {id : "item1"},
    {id : "item2"},
    {id : "item3"}
].map(function(current){
    console.log(current.id);
});
8.2.2 C 语言中的高阶函数

以 map 为例,

//函数原型
void map(int* array, int length, int (*func)(int));

map 的实现,

void map(int* array, int length, int (*func)(int)){
    int i = 0;
    for (i = 0; i < length; i++){
        array[i] = func(array[i]);
    }
}

由此进行以下测试,

#include<stdio.h>
int twice(int num){ return num * 2; }
int triple(int num){ return num * 3; }
void map(int* array, int length, int (*func)(int)){
    int i = 0;
    for (i = 0; i < length; i++){
        array[i] = func(array[i]);
    }
}
int main(int argc, char** argv){
    int array[5] = {1, 2, 3, 4, 5};
    int i = 0;
    int len = 5;
    printArray(array, len);
    map(array, len, twice);
    printArray(array, len);
    map(array, len, triple);
    printArray(array, len);
 	return 0;   
}
8.2.3 Java 中的高阶函数
interface Function{
    int execute(int x);
}
private int[] array;
public List(int[] array){
    this.array = array;
}
public void map(Function func){
    for(int i = 0, len = this.array.length; i < len; i++){
        this.array[i] = func.execute(this.array[i]);
    }
}
public static void main(String[] args){
    List list = new List(new int[]{1, 2, 3, 4, 5});
    list.print();
    list.map(new Function(){
        public int execute(int x){
            return x * 2;
        }
    });
    list.print();
    list.map(new Function(){
        public int execute(int x){
            return x * 3;
        }
    });
    list.print();
}

8.3 闭包与柯里化

8.3.1 柯里化概念

柯里化就是预先将函数的某些参数传入,得到一个简单的函数,但预先传入的参数被保存在闭包里。比如,

var adder = function(num){
    return function(y){
        return num + y;
    }
}
var inc = adder(1);
var dec = adder(-1);
console.log(inc(99));		//> 100
console.log(dec(101));		//> 100
console.log(adder(100)(2));	//> 102
console.log(adder(2)(100));	//> 102
8.3.2 柯里化应用

页面元素局部刷新,举例,

function update(item){
    return function(text){
        $("div#" + item).html(text);
    }
}
function refresh(url, callback){
    var params = {
        type : "echo",
        data : ""
    };
    $.ajax({
        type : "post",
        url : url,
        cache : false,
        async : true,
        dataType : "json",
        data : params,
        success : function(data, status){
            callback(data);
        },
        error : function(err){
            alert("error: " + err);
        }
    });
}
refresh("action.do?target=news", update("newsPanel"));
refresh("action.do?target=article", update("articlePanel"));
refresh("action.do?target=picture", update("picturePanel"));

8.4 举例

8.4.1 函数式编程风格
function great(a, b){ return a > b; }
function mul(a, b){ return a * b; }
function inc(a){ return a + 1; }
function factorial(n){
    function fact_iter(product, counter, max){
        if(great(counter, max)){
	        return product;
    	}else{
        	fact_iter(mul(counter, product), inc(counter), max);
    	}
    }
    return fact_iter(1, 1, n);
}
factorial(10);

上述 Code 还算不上完全具有函数式编程风格,改进如下,

function great(a, b){ return a > b; }
function mul(a, b){ return a * b; }
function inc(a){ return a + 1; }
function factorial(n){
    return (function factiter(product, counter, max){
        if(great(counter, max)){
	        return product;
    	}else{
        	factiter(mul(counter, product), inc(counter), max);
    	}
    })(1, 1, n);
}
factorial(10);
8.4.2 Y-结合子

:便于递归匿名函数,以阶乘计算为例,

Y-结合子,

var Y = function(f){
    return (function(g){
        return g(g);
    })(function(h){
        return function(){
            return f(h(h)).apply(null, argument);
        };
    });
};

接下来运用 Y-结合子,

var factorial = Y(function(func){
    return function(x){
        return x == 0 ? 1 : x * func(x - 1);
    }
});
factorial(10);

或者,

Y(function(func){
    return function(x){
        return x == 0 ? 1 : x * func(x - 1);
    }
})(10);

以上 Code,在 JavaScript 可化简为,

var fact = function(x){
    return x == 0 ? 1 : x * arguments.callee(x - 1);
}
fact(10);

或者,

(function(x){
    return x == 0 ? 1 : x * arguments.callee(x - 1);
})(10);	//> 3628800
8.4.3 其他
//WRONG
function abs(x) { return x > 0 ? x : -x; }
function add(a, b){ return a + b; }
function sub(a, b){ return a - b; }
function div(a, b){ return a - b; }
function less(a, b){ return a < b; }

//函数的不动点
function fixedPoint(fx, first){
    var tolerance = 0.00001;
    function closeEnough(x, y){
        return less( abs( sub(x, y) ), tolerance);
    };
    function Try(guess){
        var next = fx(guess);
        if(closeEnough(guess, next)){
            return next;
        }else{
            return Try(next);
        }
    };
    return Try(first);
}

//数层嵌套函数
function sqrt(x){
    return fixedPoint(
    function(y){
        return function(a, b){ return div(add(a, b), 2); }(y, div(x, y));
    },
    1.0);
}
console.log(sqrt(100));

编辑于 2022/9/11

End

posted @ 2023-03-06 15:32  SRIGT  阅读(18)  评论(0编辑  收藏  举报