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
本文作者:SRIGT
本文链接:https://www.cnblogs.com/SRIGT/p/17184108.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步