// 转换成驼峰表示法
function combo(msg) {
var arr = msg.split("-");
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1);
}
arr[0] = arr[0].toLowerCase();
msg = arr.join("");
return msg;
};
console.log(combo("get-element-by-id"));
// 格式化日期成YYYY-MM-DD形式
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth() + 1;
month = month < 10 ? '0' + month : month;
var day = d.getDate();
day = day < 10 ? '0' + day : day;
console.log(year + '-' + month + '-' + day);
// 合并两个数组,并删除第二个元素
var arr1 = [1, 2, 3, 4, 5, 6];
var arr2 = ['a', 'b', 'c', 'd', 'e', 'f'];
var arr3 = arr1.concat(arr2);
arr3.splice(1, 1);
console.log(arr3);
// 去除字符串里面的HTML标签
var str = "<div>这里是div<p>里面的段落</p></div>";
var reg = /<\/?\w+\/?>/gi;
console.log(str.replace(reg, ""));
// 求字符串中出现次数最多的一个字符
var str = 'asdfssaaasasasasaa';
var json = {};
console.log(json[str.charAt(0)]);
for (var i = 0; i < str.length; i++) {
if (!json[str.charAt(i)]) {
json[str.charAt(i)] = 1;
} else {
json[str.charAt(i)]++;
}
};
var iMax = 0;
var iIndex = '';
for (var i in json) {
if (json[i] > iMax) {
iMax = json[i];
iIndex = i;
}
}
console.log('出现次数最多的是:' + iIndex + '出现' + iMax + '次');
// 闭包的写法:其实就是方法里面又写了一个方法
function outer() {
var num = 1;
function inner() {
var n = 2;
console.log(n + num);
}
return inner;
}
outer()();
// 获取浏览器可视范围
var w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
console.log(w);
var h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
console.log(h);
// 每3位一个逗号分隔钱数--仅适用没有小数位的情况
function re(str) {
str += '';
return str.split("").reverse().join("");
}
function toRMB(num) {
var tmp = '';
for (var i = 1; i <= re(num).length; i++) {
tmp += re(num)[i - 1];
if (i % 3 == 0 && i != re(num).length) {
tmp += ',';
}
}
return re(tmp);
}
toRMB(1234567890.005);
// 接收一个数字(字符串类型),将数字每3位一组以逗号分隔
function outputmoney(number) {
number = number.replace(/\,/g, "");
if (isNaN(number) || number == "") return "";
number = Math.round(number * 100) / 100;
if (number < 0)
return '-' + outputdollars(Math.floor(Math.abs(number) - 0) + '') + outputcents(Math.abs(number) - 0);
else
return outputdollars(Math.floor(number - 0) + '') + outputcents(number - 0);
}
//格式化金额
function outputdollars(number) {
if (number.length <= 3)
return (number == '' ? '0' : number);
else {
var mod = number.length % 3;
var output = (mod == 0 ? '' : (number.substring(0, mod)));
for (i = 0; i < Math.floor(number.length / 3); i++) {
if ((mod == 0) && (i == 0))
output += number.substring(mod + 3 * i, mod + 3 * i + 3);
else
output += ',' + number.substring(mod + 3 * i, mod + 3 * i + 3);
}
return (output);
}
}
function outputcents(amount) {
amount = Math.round(((amount) - Math.floor(amount)) * 100);
return (amount < 10 ? '.0' + amount : '.' + amount);
}
// 随机5个不一样的数
var num1 = [];
for (var i = 0; i < 5; i++) {
num1[i] = Math.floor(Math.random() * 10) + 1; //范围是 [1, 10]
for (var j = 0; j < i; j++) {
if (num1[i] == num1[j]) {
i--;
}
}
}
console.log(num1);
// 去除数组中重复的元素,方法一:
Array.prototype.unique = function() {
var len = this.length,
newArr = [],
flag = 1;
for (var i = 0; i < len; i++, flag = 1) {
for (var j = 0; j < i; j++) {
if (this[i] == this[j]) {
flag = 0; //找到相同的数字后,不执行添加数据
}
}
flag ? newArr.push(this[i]) : '';
}
return newArr;
}
var arr = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 5, 3, 6];
console.log(arr.unique());
// 去除数组中重复的元素,方法二:
var arr = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 5, 3, 7];
Array.prototype.unique2 = function() {
var n = []; //一个新的临时数组
for (var i = 0; i < this.length; i++) { //遍历当前数组
//如果当前数组的第i已经保存进了临时数组,那么跳过,
//否则把当前项push到临时数组里面
if (n.indexOf(this[i]) == -1) n.push(this[i]);
}
return n;
}
var newArr2 = arr.unique2(arr);
console.log(newArr2);
// 写一个方法,清除字符串前后的空格,要兼容各种浏览器
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^\s+/, "").replace(/\s+$/, "");
}
}
var str = " \t\n test string ".trim();
console.log(str == "test string");
// 获取页面中所有的checkbox(不使用第三方框架)
var domList = document.getElementsByTagName('input')
var checkBoxList = [];
var len = domList.length;
for (var i = 0; i < len; i++) {
if (domList[i].type == 'checkbox') {
checkBoxList.push(domList[i]);
}
}
// 阶乘
Number.prototype.N = function() {
var re = 1;
for (var i = 1; i <= this; i++) {
re *= i;
}
return re;
}
var num = 5;
console.log(num.N());
// 定时器的执行时间测试
var a = 6;
setTimeout(function() {
var a = 666;
console.log(a); // 输出666,
}, 1000);
var a = 6;
setTimeout(function() {
console.log(a); // 输出undefined
var a = 666;
}, 1000);
var a = 6;
setTimeout(function() {
console.log(a);
var a = 66;
}, 0);
a = 666;
console.log(a); // 666, undefined;
// 看下面代码各自输出什么
function foo() {
foo.a = function() { console.log(1) };
this.a = function() { console.log(2) };
a = function() { console.log(3) };
var a = function() { console.log(4) };
};
foo.prototype.a = function() { console.log(5) };
foo.a = function() { console.log(6) };
foo.a(); //6
var obj = new foo();
obj.a(); //2
foo.a(); //1
var a = 5;
function test() {
a = 0;
console.log(a);
console.log(this.a); //没有定义a这个属性
var a;
console.log(a)
}
test(); // 0, 5, 0
new test(); // 0, undefined, 0 //由于类它自身没有属性a,所以是undefined
var bool = !!2;
console.log(bool); //true; 双向非操作可以把字符串和数字转换为布尔值
var obj = {
name: 'leipeng',
showName: function() {
console.log(this.name);
}
}
obj.showName(); // leipeng
function Parent(name, money) {
this.name = name;
this.money = money;
this.info = function() {
console.log('姓名: ' + this.name + ' 钱: ' + this.money);
}
}
//定义孩子类
function Children(name) {
Parent.call(this, name); //继承 姓名属性,不要钱。
this.info = function() {
console.log('姓名: ' + this.name);
}
}
//实例化类
var per = new Parent('parent', 800000000000);
var chi = new Children('child');
per.info();
chi.info();
var t = true;
setTimeout(function() {
console.log(123);
t = false;
}, 1000);
console.log("开始");
while (t) { console.log("sfs"); }
console.log("结束");
console.log('end'); // 这句永远执行不到
// test是一个ul的id,ul有4个li,点击li获取li的下标
window.onload = function() {
var lis = document.getElementById("test").children;
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = (function(i) {
return function() {
alert(i);
}
})(i);
}
};
// 加法计算器:可以输入任意多个值
function sum() {
var result = 0;
var arr = arguments;
for (var i = 0; i < arr.length; i++) {
var num = arguments[i];
if (typeof num == 'number') {
result += num;
} else {
result = '必须全部为数字';
};
};
return result;
}
console.log(sum(1, 2, 3));
console.log(sum(-1, 2, 3, '4'));
var User = {
count: 1,
getCount: function() {
return this.count;
}
};
console.log(User.getCount());
var func = User.getCount;
console.log(func());
var iNum = 0;
for (var i = 1; i < 10; i++) {
if (i % 5 == 0) {
continue;
}
iNum++;
}
console.log(iNum); // 8
var obj = { proto: { a: 1, b: 2 } };
function F() {};
F.prototype = obj.proto;
var f = new F();
obj.proto.c = 3;
obj.proto = { a: -1, b: -2 };
console.log(f.a); // 1
console.log(f.c); // 3
delete F.prototype['a'];
console.log(f.a); // undifined
console.log(obj.proto.a); // -1
// 给String对象添加一个方法,传入一个string类型的参数,然后将字符串以空格间隔返回
String.prototype.spacify = function() {
return this.split('').join(' ');
};
"hello world!".spacify();
function setName() {
var name = "张三"; // 不加var也一样
};
setName();
console.log(name); // 张三
var b = 2;
function test2() {
window.b = 3;
console.log(b);
};
test2(); // 3
c = 5; //声明一个全局变量c
function test3() {
window.c = 3;
console.log(c); //答案:undefined,原因:由于此时的c是一个局部变量c,并且没有被赋值
var c;
console.log(window.c); //答案:3,原因:这里的c就是一个全局变量c
}
test3();
function foo(a) {
arguments[0] = 2;
console.log(a); //答案:2,因为:a、arguments是对实参的访问,b、通过arguments[i]可以修改指定实参的值
}
foo(1);
function foo(a) {
console.log(arguments.length); //答案:3,因为arguments是对实参的访问
}
foo(1, 2, 3);
// 执行顺序:test()先执行
function test() {
console.log("test函数");
}
setTimeout(function() {
console.log("定时器回调函数");
}, 0)
test();
// JS高级--原型继承
function Parent() {
this.name = 'wang';
}
function Child() {
this.age = 28;
}
Child.prototype = new Parent(); //继承了Parent,通过原型
var demo = new Child();
console.log(demo.age);
console.log(demo.name); //得到被继承的属性
(function test() {
var a = b = 5;
alert(typeof a);
alert(typeof b);
})();
console.log(typeof a);
console.log(typeof b); // Number Number undifined number
// JS高级
function parseInt(str, radix) {
return str + '-' + radix;
};
var a = ["1", "2", "3"];
a.map(parseInt); // ["1-0", "2-1", "3-2"] 不能大于radix
function say667() {
// Local variable that ends up within closure
var num = 666;
var sayAlert = function() {
console.log(num);
}
num++;
return sayAlert;
}
var sayAlert = say667();
sayAlert(); //执行结果应该弹出的667
// 找出字符串中重复次数最多的一个字符
var str = 'shdshdfjkfjfdgjkjdksgjskdjfsfsfsfjksjkfdkjf';
var arr = str.split('').sort() //把字符串变为数组,在排序
str = arr.join('') //把排序好的数组,变为字符串
var count = 0 //不能使用undefined,转化为数字是NaN
var char = 0
var reg = /(\w)\1+/g
str.replace(reg, function(parent, son) {
if (parent.length > count) {
count = parent.length
char = son
}
})
console.log("最多的字符为:" + char + ";个数为:" + count);
function Foo() {
getName = function() { console.log(1); };
return this;
}
Foo.getName = function() { console.log(2); };
Foo.prototype.getName = function() { console.log(3); };
var getName = function() { console.log(4); };
function getName() { console.log(5); }
//请写出以下输出结果:
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3
function f(y) {
var x = y * y;
return x;
}
for (x = 0; x < 5; x++) {
y = f(x);
console.log(y); // 0 1 4 9 16
}
// 代码演示jQuery.extend方法和jQuery.fn.extend方法的区别
jQuery.extend({
liu: function() {
console.log("liu");
}
});
$.liu();
;(function($) {
$.fn.extend({
zhang: function() {
console.log("zhang");
}
});
})(jQuery);
$(document).ready(function() {
$("#btn4").zhang();
});
// 编写两个jQuery的扩展,实现将数组转化为json字符串,然后再转化回来
$.fn.stringifyArray = function(array) {
return JSON.stringify(array);
};
$.fn.parseArray = function(array) {
return JSON.parse(array);
};
var arr = [1,2,3,4,5];
console.log($("").stringifyArray(arr));
var str = '{"name":"huangxiaojian","age":"23"}';
var jsonObj = JSON.parse(str);
console.log(jsonObj);// 转换成json对象
var jsonStr = JSON.stringify(jsonObj);
console.log(jsonStr);// 转换成json字符串
// 扩展原生JS,实现在数组指定位置插入元素
Array.prototype.insert = function (index,item){
this.splice(index,0,item);
};
var nums = ["1","2","4"];
nums.insert(2,"3");
console.log(nums);
// 数组去重
var arr = [2, 2, 3, 3, 4, 4, 5, 5, 6, 1, 9, 3, 25, 4];
function deRepeat(array) {
var newArr = [];
var obj = {};
var index = 0;
var l = array.length;
for (var i = 0; i < l; i++) {
if (obj[array[i]] == undefined) {
obj[array[i]] = 1;
newArr[index++] = array[i];
} else if (obj[array[i]] == 1)
continue;
}
return newArr;
};
console.log(deRepeat(arr)); //输出2,3,4,5,6,1,9,25
// 每半秒叫一声
function Dog() {
this.wow = function() {
console.log('Wow');
}
this.yelp = function() {
this.wow();
}
};
function MadDog() {
this.yelp = function() {
var self = this;
setInterval(function() {
self.wow();
}, 500);
}
}
MadDog.prototype = new Dog();
var dog = new Dog();
dog.yelp();// 叫一声
var madDog = new MadDog();
madDog.yelp();// 每半秒叫一声
// 定义一个动物类
function Animal (name) {
this.name = name || 'Animal';
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
// 自定义一个模块
(function(window){
var msg = 'atguigu.com';
function showMsg(){
console.log(msg);
}
window.myModule = {
showMsg : showMsg()
};
})(window)
// 注册“click”事件处理函数更高级的一种方法:
window.onload = function(){
var images = document.getElementsByTagName("img");
for (var i = 0;i < images.length; i++) {
var image = images[i];
if(image.addEventListener) // 注册事件处理程序的另一种方法
image.addEventListener("click",hide,false);
else // 兼容IE8以及以前的版本
image.attachEvent("onclick",hide);
}
// 这便是上面注册的事件处理函数
function hide(event){ event.target.style.visibility = "hidden";}
};
尽管RegExp并不是语言中的基本数据类型,但是它们依然具有直接量的写法,可以直接在JS程序中使用。在两条
斜线之间的文本构成了一个正则表达式直接量。第二条斜线之后也可以跟随一个或多个字母,用来修饰匹配模式
的含义。
对象引用:
var a = [];
var b = a; // a、b两个变量引用了同一个数组
b[0] = 1; // 通过变量b来修改引用的数组
a[0] // 1:变量a也会修改
a === b; // true:a和b引用同一个数组,因此它们相等
将对象(或数组)赋值给一个变量,仅仅是赋值的引用值:对象本身并没有复制一次。如果想得到一个对象或数组
的副本,则必须显示复制对象的每个属性或数组的每个元素。例如,通过循环来完成数组复制:
var a = ['a','b','c'];
var b = [];
for(var i = 0; i < a.length; i++){
b[i] = a[i];
}
比较两个数组是否相等的函数:
function equaryArrays(a,b){
if(a.length != b.length) return false;// 两个长度不等的数组不相等
for(var i = 0; i < a.length; i++){
if(a[i] !== b[i]) return false; // 如果有任意元素不等,则数组不等
return true; // 否则它们相等
}
};
// 使用for循环遍历链表数据结构,并返回链表中的最后一个对象(也就是第一个不包含next属性的对象)
function tail(o){ // 返回链表的最后一个节点对象
for(; o.next; o = o.next); // 根据判断o.next是不是真值来执行遍历
return o;
};
// 可以使用下面这段代码将所有对象属性复制到一个数组中
var 0 = {x:1, y:2, z:3};
var a = [], i = 0;
for(a[i++] in 0);
// JS数组不过是一种特殊的对象,因此for/in循环可以像枚举对象属性一样枚举数组索引。
// 枚举数组的索引0、1、2
for(i in a) console.log(i);
// 在循环中,不论出于什么原因,只要不想继续执行整个循环,就可以用break来提前退出。
// 当循环终止条件非常复杂时,在函数体内使用break语句实现这些条件判断的做法要比直接
// 在循环表达式中写出这个复杂终止条件的做法简单很多。例如下面的循环遍历整个数组元素来
// 查找某个特定的值,如果提前找到要的数组元素,则使用break语句退出循环:
for(var i = 0; i < a.length; i++){
if(a[i] == target) break;
}
// 属性包括名字和值。属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。
// 值可以是JS值,或者可以是一个getter或setter函数(或两者都有)。除了名字和值之外,每个属性还有
// 一些与之相关的值,称为“属性特性”:
// 可写:表明是否可以设置该属性的值
// 可枚举:表明是否可以通过for/in循环返回该属性
// 可配置:表明是否可以删除或修改该属性
// 除了包含属性之外,每个对象还拥有三个相关的对象特性:
// 对象的原型:指向另外一个对象,本对象的属性继承自它的原型对象
// 对象的类:是一个标识对象类型的字符串
// 对象的扩展标记:指明了是否可以向该对象添加新的属性
// 三类JS对象和两类属性作区分:
// 内置对象:由ES规范定义的对象或类,比如数组、函数、日期和正则表达式
// 宿主对象:由JS解释器所嵌入的宿主环境(如WEB浏览器)定义的。客户端JS中表示网页结构的HTMLElement对象
// 均是宿主对象。宿主对象也可以当成内置对象
// 自定义对象:由运行中的JS代码创建的对象
// 自有属性:是直接在对象中定义的属性
// 继承属性:是在对象的原型对象中定义的属性
// 对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候
// 也都会计算它的每个属性的值。也就是说,如果在一个重复调用的函数中的循环体内使用了对象直接量它将创建
// 很多新对象,并且每次创建的对象的属性值也有可能不同。
// 通过原型继承创建一个新对象
// inherit()返回一个继承自原型对象p的属性的新对象
// 这里使用ES5中的Object.create()函数(如果存在的话)
// 如果不存在Object.create(),则退化使用其他方法
function inherit(p){
if(p == null) throw TypeError(); // p是一个对象,但不能是null
if(Object.create)
return Object.create(p); // 如果Object.create()存在,直接使用它
var t = typeof p; // 否则进一步检测
if(t !== "object" && t!== "function")
throw TypeError();
function f(){}; // 定义一个空构造函数
f.prototype = p; // 将其原型属性设置为P
return new f(); // 使用f()创建p的继承对象
}
// 通过原型链实现属性的继承
var o = {}; // o从Object.prototype继承对象的方法
o.x = 1; // 给o定义一个属性x
var p = inherit(o); // p继承o和Object.prototype
p.y = 2; // 给p定义一个属性y
var q = inherit(p); // q继承p、o和Object.prototype
q.z = 3; // 给q定义一个属性z
var s = q.toString(); // toString继承自Object.prototype
q.x + q.y // 3:x和y分别继承自o和P
// 在JS中,只有在查询属性时才会体会到继承的存在,而设置属性则和继承无关
var unitcircle = {r:1}; // 一个用来继承的对象
var c = inherit(unitcircle);// c继承属性r
c.x = 1; c.y = 1; // c定义了两个属性
c.r = 2; // c覆盖继承来的属性
unitcircle.r; // 1:原型对象没有修改
// 除非确定book和book.subtitle都是对象,否则不能写表达式book.subtitle.length,因为这样会报错
// 为避免出错,一种简练且常用的方法,获取subtitle的length属性或undefined
var len = book && book.subtitle && book.subtitle.length;
// 检测属性
// in运算符,左侧是属性名(字符串),右侧是对象。如果对象的自有属性或继承属性中包含这个属性则返回true
var o = {x:1}
"x" in o; // true
"y" in o; // false
"toString" in o; // true:o继承toString属性
// 对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false
var o = {x:1};
o.hasOwnProperty("x"); // true:o有一个自有属性x
o.hasOwnProperty("y"); // false:o中不存在属性y
o.hasOwnProperty("toString");// false:toString是继承属性
// propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性且这个属性的可枚举性为
// true时,它才会返回true。
var o = inherit({y:2});
o.x = 1;
o.propertyIsEnumerable("x"); // true:o有一个可枚举的自有属性x
o.propertyIsEnumerable("y"); // false:y是继承来的
Object.prototype.propertyIsEnumerable("toString");// false:不可枚举
// for/in循环可以在循环体中遍历对象中所有的可枚举的属性(包括自有属性和继承的属性),但对象继承的内置
// 方法不可枚举
var o = {x:1, y:2, z:3}; // 3个可枚举的自有属性
o.propertyIsEnumerable("toString"); // false:不可枚举
for(p in o)
console.log(p); // 遍历输出x、y、z,不会输出toString
for(p in o){
if(!o.hasOwnProperty(p)) continue; // 跳过继承的属性
}
for(p in o){
if(typeof o[p] === "function") continue;// 跳过方法
}
// 返回一个数组,这个数组包含的是可枚举的自有属性的名字
function keys(o){
if(typeof o !== "object") throw TypeError();// 参数必须是对象
var result = [];
for(var prop in o){ // 遍历所有可枚举的属性
if(o.hasOwnProperty(prop))// 判断是否是自有属性
result.push(prop);// 如果是自有属性,把属性名添加到数组中
}
return result;
}
// ES5中定义了两个用于枚举属性名称的函数。第一个是Object.keys(),它返回一个数组,这个数组由对象中
// 可枚举的自有属性的名称组成,工作原理和keys()类型。
// 第二个枚举属性的函数是Object.getOwnPropertyNames(),它和Object.keys()类似,只是它返回对象的所有
// 自有属性的名称,而不仅仅是可枚举的属性。
// 除了保护名字和值之外,属性还包含一些标识它们可写、可枚举和可配置的特性。
// 掌握ES5中查询和设置这些属性特性的API对于库的开发者来说非常重要,因为:
// 1.可以通过这些API给原型对象添加方法,并将它们设置成不可枚举的,这让它们看起来更像内置方法。
// 2.可以通过这些API给对象定义不能修改或删除的属性,借此“锁定”这个对象。
// 可以通过Object.getOwnPropertyDescriptor()获得某个对象特定属性的属性描述符
Object.getOwnPropertyDescriptor({x:1},"x");//返回{value:1,writable:true,enumerable:true,configurable:true}
//返回{get:/*func*/,set:undefined,enumerable:true,configurable:true}
Object.getOwnPropertyDescriptor(random,"octet");
// 对于继承属性和不存在的属性,返回undefined
Object.getOwnPropertyDescriptor({},"x");// undefined,没有这个属性
Object.getOwnPropertyDescriptor({},"toString");// undefined,继承属性
// 定义存取器属性最简单的方法是使用对象直接量语法的一种扩展写法:
var o = {
// 普通的数据属性
data_prop:value,
// 存取器属性都是成对定义的函数
get accessor_prop(){/*这里是函数体*/},
set accessor_prop(value){/*这里是函数体*/}
};
// 存取器属性定义为一个或两个和属性同名的函数,这个函数定义没有使用funciton关键字,而是使用get和(或)set。注意,这里
// 没有使用冒号将属性名和函数体分隔开,但在函数体的结束和下一个方法或数据属性之间有逗号分隔。
var p = {
// x和y是普通的可读写的数据属性
x:1.0,
y:1.0,
// r是可读写的存取器属性,它有getter和setter
// 函数体结束后不要忘记带上逗号
get r(){ return Math.sqrt(this.x * this.x + this.y * this.y);},
set r(newvalue){
var oldvalue = Math.sqrt(this.x * this.x + this.y * this.y);
var ratio = newvalue/oldvalue;
this.x *= ratio;
this.y *= ratio;
},
// theta是只读存取器属性,它只有getter方法
get theta(){ return Math.atan2(this.y, this.x);}
};
// 复制属性的特性
// 给Object.prototype添加一个不可枚举的extend()方法
// 这个方法继承自调用它的对象,将作为参数传入的对象的属性一一复制
// 除了值之外,也复制属性的所有特性,除非在目标对象中存在同名的属性,
// 参数对象的所有自有对象(包括不可枚举的属性)也会一一复制。
Object.defineProperty(Object.prototype,
"extend", // 定义Object.prototype.extend
{
writable:true,
enumerable:false, // 将其定义为不可枚举
configurable:true,
value:function(o){ // 值就是这个函数
// 得到所有的自有属性,包括不可枚举属性
var names = Object.getOwnPropertyNames(o);
// 遍历它们
for(var i = 0; i < names.length; i++){
// 如果属性已经存在,则跳过
if(names[i] in this) continue;
// 获得o中的属性的描述符
var desc = Object.getOwnPropertyDescriptor(o,names[i]);
// 用它给this创建一个属性
Object.defineProperty(this,names[i],desc);
}
}
}
);
// 对象的原型属性是用来继承属性的,这个属性如此重要,以至于我们经常把“o的原型属性”直接叫做“o的原型”。
// 通过对象直接量创建的对象使用Object.prototype作为它们的原型,通过new创建的对象使用构造函数的prototype属性作为它们的
// 原型,通过Object.create()创建的对象使用第一个参数(可以是null)作为它们的原型。
// classof()函数可以返回传递给它的任意对象的类
function classof(o){
if(o === null) return "Null";
if(o === undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
};
classof(123);
// 所有的索引都是属性名,但只有在0~2^32-2之间的整数属性名才是索引。所有的数组都是对象,可以为其创建任意名字的属性。但如果
// 使用的属性是数组的索引,数组的特殊行为就是将根据需要更新它们的length属性值。
// 注意:可以使用负数或非整数来索引数组。这种情况下,数值转换为字符串,字符串作为属性名来用。既然名字不是非负整数,它就只能
// 当做常规的对象属性,而非数组的索引。同样,如果凑巧使用了非负整数的字符串,它就当做数组索引,而非对象属性。
a[-1.23] = true; // 这将创建一个名为“-1.23”的属性
a["1000"] = 0; // 这是数组的第1001个元素
a[1.000] // 和a[1]相等
// 事实上数组索引仅仅是对象属性名的一种特殊类型,这意味着JS数组没有“越界”错误的概念。当试图查询任何对象中不存在的属性时,
// 不会报错,只会得到undefined值。类似于对象,对于对象同样存在这种情况。
// 如果是稀疏数组,可以用下面方法遍历。不存在的索引不会遍历到。
for(var index in sparseArray){
var value = sparseArray[index];
// 此处可以使用索引和值做一些事情
}
// 数组的forEach()方法可以用于遍历数组。它有三个参数:数组元素、元素索引、数组本身。也可有一个。
var data = [1,2,3,4,5]; // 要求和的数组
// 计算数组元素的和值
var sum = 0;
data.forEach(function(value){ sum += value;});// 将每个值累加到sum上
sum // => 15
// 每个数组元素的值自加1
data.forEach(function(v,i,a){ a[i] = v + 1;});
data // => [2,3,4,5,6]
// 注意:forEach()无法在所有元素都传递给调用的函数之前终止遍历。如果要提前终止,要把forEach()方法放在一个try块中
// 详情参见犀牛书169页。
// map()方法将调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。例如:
a = [1,2,3];
b = a.map(function(x){return x*x;}); // b是[1,4,9]
// filter()方法返回的数组元素是调用的数组的一个子集。
a = [5,4,3,2,1];
smallValues = a.filter(function(x){return x < 3}); // [2,1]
everyother = a.filter(function(x,i){return i%2 == 0});
console.log(everyother);// [5,3,1]
// every()和some()方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回true或false
// every()方法是“针对所有”,当且仅当数组中的所有元素调用判定函数都返回true,它才返回true:
a = [1,2,3,4,5];
a.every(function(x){return x < 10;}) // =>true:所有的值都<10
a.every(function(x){return x % 2 === 0;})// =>false:不是所有的值都是偶数
// some()方法是“存在”,当数组中至少有一个元素调用判定函数返回true,它就返回true,所有元素调用判定函数返回false,
// 它才返回false
a = [1,2,3,4,5];
a.some(function(x){return x%2 === 0;}) // =>true:a含有偶数值
a.some(isNaN); // =>false:a不包含非数值元素
// 注意:一旦every()和some()确认该返回什么值它们就会停止遍历数组元素。另外,根据数学上的惯例,在空数组上调用时,
// every()返回true,some()返回false
// reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值。这在函数式编程中是常见的操作,也可以称为
// "注入"和"折叠"
var a = [1,2,3,4,5];
var sum = a.reduce(function(x,y){return x+y},0);// 数组求和
var product = a.reduce(function(x,y){return x*y},1);// 数组求积
var max = a.reduce(function(x,y){return (x>y)?x:y;});// 求最大值
// reduce()需要两个参数,第一个是执行化简操作的函数,第二个是一个传递给函数的初始值。如果不指定初始值,它将使用数组的
// 第一个元素作为其初始值。
// reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低处理数组。
// indexOf()和lastIndexOf()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回-1。
// indexOf()从头至尾搜索,而lastIndexOf()则反向搜索。
// 第一个参数是需要搜索的值,第二个参数是指定一个索引,从哪开始搜索
// 在数组中查找所有出现的x,并返回一个保护匹配索引的数组
function findall(a,x){
var results = [], // 将会返回的数组
len = a.length, // 待搜索数组的长度
pos = 0; // 开始搜索的位置
while(pos < len){ // 循环搜索多个元素
pos = a.indexOf(x,pos);// 搜索
if(pos === -1) break; // 未找到就完成搜索
results.push(pos); // 否则,在数组中存储索引
pos = pos + 1; // 并从下一个位置开始搜索
}
return results; // 返回包含索引的数组
}
// 判断数组类型:typeof操作符对数组返回“对象”,instanceof操作符只能用于简单的情形,而下面方法更可靠
Array.isArray([]) // true
Array.isArray({}) // false
// 如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的
// 上下文,也就是该函数的this的值。
// JS的函数可以嵌套在其他函数中定义,这样它们就可以访问它们被定义时所处的作用域中的任何变量。这意味着JS函数构成了一个闭包。
// 计算阶乘的递归函数
function factorial(x){
if(x <= 1) return 1;
return x * factorial(x-1);
}
// 和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值
// 指向调用它的对象。如果嵌套函数作为函数调用,其this值不少全局对象(非严格模式下)就是undefined(严格模式下)。很多人误认为
// 调用嵌套函数时this会指向调用外层函数的上下文。如果你想访问这个外部函数的this值,需要将this的值保存在一个变量里,这个
// 变量和内部函数都同在一个作用域内。通常使用变量self来保存this,比如:
var o = { // 对象o
m:function(){ // 对象中的方法m()
var self = this; // 将this的值保存至一个变量中
console.log(this === o);// 输出true,this就是这个对象o
f(); // 调用辅助函数f()
function f(){
console.log(this === o);// false:this的值是全局对象或undefined
console.log(self === o);// true:self指外部函数的this值
}
}
};
o.m(); // 调用对象o的方法m()
// 凡是没有形参的构造函数调用都可以省略圆括号,比如下面两行代码就是等价的:
var o = new Object();
var o = new Object;
// JS中的函数也是对象,和其他JS对象没有两样,函数对象也可以包含方法。其中的两个方法call()和apply()可以用来间接地调用函数。
// 两个方法都允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法
// 假设定义了函数f,它的实参只有一个x。如果调用这个函数时传入两个实参,第一个实参可以通过参数名x来获得,也可以通过
// arguments[0]来得到。第二个实参只能通过arguments[1]来得到。此外,和真正的数组一样,arguments也包含一个length属性,用以标识
// 其所包含的元素的个数。因此,如果调用函数f()时传入两个参数,arguments.length的值就是2.
function f(x,y,z){
// 首先,验证传入实参的个数是否正确
if(arguments.length != 3){
throw new Error("function f called with" + arguments.length + "arguments,but it expects 3 arguments.");
}
// 再执行函数的其他逻辑.....
}
function f(x){
console.log(x); // 3
arguments[0] = null;
console.log(x); // null
}
f(3);
// callee和caller属性:ES5严格模式中,对这两个属性的读写操作会产生一个类型错误。而在非严格模式下,ES标准规范规定
// callee属性指代当前正在执行的函数。caller是非标准的,但大多数浏览器都实现了这个属性,它指代调用当前正在执行的函数的
// 函数。通过caller属性可以访问调用栈。callee属性在某些时候会非常有用,比如在匿名函数中通过callee来递归地调用自身。
var factorial = function(x){
if(x <= 1) return 1;
return x * arguments.callee(x-1);
}
// 将对象属性用作实参
// 当一个函数包含超过三个形参时,要记住调用函数中实参的正确顺序并不容易。最好的方法是通过键值对的形式来传入参数,这样
// 参数的顺序就无关紧要了。为了实现这种风格的方法调用,定义函数的时候,传入的实参都写入一个单独的对象之中,在调用的时候
// 传入一个对象,对象中的键值对是真正需要的实参数据。
function arraycopy(from,from_start,to,to_start,length){
// 逻辑代码
}
function easycopy(args){
arraycopy(args.from,
args.from_start || 0, // 注意这里设置了默认值
args.to,
args.to_start || 0,args.length);
}
// 来看如何调用easycopy()
var a = [1,2,3,4], b = [];
easycopy({from: a, to: b, length: 4});
// 除了可以将函数赋值给变量,同样可以将函数赋值给对象的属性。当函数作为对象的属性调用时,函数就称为方法:
var o = {square: function(x){ return x * x;}}; // 对象直接量
var y = o.square(16); // y等于256
// 函数甚至不需要带名字,当把它们赋值给数组元素时:
var a = [function(x){return x*x;}, 20]; // 数组直接量
console.log(a[0](a[1])); // 400
// 我们将作用域链描述为一个对象列表,不是绑定的栈。每次调用JS函数的时候,都会为之创建一个新的对象用来保存局部变量,把
// 这个对象添加至作用域链中。当函数返回的时候,就从作用域链中将这个绑定变量的对象删除。如果不存在嵌套的函数,也没有其他
// 引用指向这个绑定对象,它就会被当做垃圾回收掉。如果定义了嵌套的函数,每个嵌套的函数都各自对应一个作用域链,并且这个
// 作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部函数中保存下来,那么它们也会和所指向的变量绑定对象一样
// 当做垃圾回收。但是如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用
// 指向这个嵌套的函数。它就不会被当做垃圾回收,并且它所指向的变量绑定对象也不会被当做垃圾回收。
function counter(){
var n = 0;
return {
count: function(){ return n++;},
reset: function(){ n = 0;}
};
}
var c = counter(), d = counter(); // 创建两个计数器
console.log(c.count()); // 0
console.log(d.count()); // 0:它们互不干扰
c.reset(); // reset()和count()方法共享状态
console.log(c.count()); // 0:因为我们重置了c
console.log(d.count()); // 1:而没有重置d
// 1.两个方法都可以访问私有变量n
// 2.每次调用counter()都会创建一个新的作用域链和一个新的私有变量。因此,如果调用counter()两次,会得到两个计数器对象,
// 并且彼此包含不同的私有变量。
// 利用闭包实现的私有属性存取器方法
// 这个函数给对象o增加了属性存取器方法
// 方法名称为get<name>和set<name>。如果提供了一个判定函数
// setter方法就会用它来检测参数的合法性,然后再存储它
// 如果判定函数返回false,setter方法抛出一个异常
// 这个函数有一个非同寻常之处,就是getter和setter函数
// 所操作的属性值并没有存储在对象o中
// 相反,这个值仅仅是保存在函数中的局部变量中
// getter和setter方法同样是局部函数,因此可以访问这个局部变量
// 也就是说,对于两个存取器方法来说这个变量是私有的
// 没有办法绕过存取器方法来设置或修改这个值
function addPrivateProperty(o,name,predicate){
var value; // 这是一个属性值
// getter方法简单地将其返回
o["get" + name] = function(){ return value;};
// setter方法首先检查值是否合法,若不合法就抛出异常
// 否则就将其存储起来
o["set" + name] = function(v){
if(predicate && !predicate(v))
throw Error("set" + name + ":invalid value " + v);
else
value = v;
};
}
// 下面的代码展示了addPrivateProperty()方法
var o = {}; // 设置一个空对象
// 增加属性存取器方法getName()和setName()
// 确保只允许字符串值
addPrivateProperty(o,"Name",function(x){ return typeof x == "string";});
o.setName("Frank"); // 设置属性值
console.log(o.getName());// 得到属性值
o.setName(o); // 试图设置一个错误类型的值
// 检测传入的参数个数:在函数体力,arguments.length表示传入函数的实参的个数。下面函数使用arguments.callee,因此它不能在
// 严格模式下工作
function check(args){
var actual = args.length; // 实参的真实个数
var expected = args.callee.length; // 期望的实参个数
if(actual !== expected){
throw Error("Expected" + expected + "args; got " + actual);
}
}
function f(x,y,z){
check(arguments); // 检查实参个数和期望的实参个数是否一致
return x + y + z; // 再执行函数的后续逻辑
}
// bind()方法:这个方法的主要作用就是将函数绑定至某个对象。
function f(y){ return this.x + y;}; // 这个是待绑定的函数
var o = {x:1}; // 将要绑定的对象
var g = f.bind(o); // 通过调用g(x)来调用o.f(x)
g(2) // 3
// 可以通过如下代码轻易地实现这种绑定:
// 返回一个函数,通过调用它来调用o中的方法f(),传递它所有的实参
function bind(f,o){
if(f.bind) return f.bind(o); // 如果bind()方法存在的话,使用bind()方法
else return function(){ // 否则,这样绑定
return f.apply(o,arguments);
}
}
// ES5中的bind()方法不仅仅是将函数绑定至一个对象,它还附带一些其他应用:除了第一个实参之外,传入bind()的实参也会绑定
// 至this,这个附带的应用是一种常见的函数式编程技术,有时也被称为“柯里化”
var sum = function(x,y){ return x + y}; // 返回两个实参的和值
// 创建一个类似sum的新函数,但this的值绑定到Null
// 并且第一个参数绑定到1,这个新的函数期望只传入一个实参
var succ = sum.bind(null,1);
succ(2) // 3:x绑定到1,并传入2作为实参y
function f(y,z){ return this.x + y + z};// 另外一个做累加计算的函数
var g = f.bind({x:1},2); // 绑定this和y
g(3) // 6:this.x绑定到1,y绑定到2,z绑定到3
// 通过Function()构造函数来定义函数
var f = new Function("x","y","return x*y;");
// 下面代码定义的函数和上面创建的函数几乎等价:
var f = function(x,y){return x*y;}
// Funcition()构造函数可以传入任意数量的字符串实参,最后一个实参所表示的文本就是函数体,它可以包含任意的JS语句,每
// 两条语句之间用分号分隔。传入构造函数的其他所有的实参字符串是指定函数的形参名字的字符串。如果定义的函数不包含任何
// 参数,只须给构造函数简单地传入一个字符串--函数体--即可。
// 关于Function()构造函数非常重要的一点:它所创建的函数并不是使用词法作用域,它的函数体代码的编译总是在顶层 函数执行
var scope = "global";
function constructFucntion(){
var scope = "local";
return new Funcition("return scope");// 无法捕获局部作用域
}
constructFucntion()();// "global"
// 使用函数处理数组:假设有一个数组,数组元素都是数字,我们想要计算这些元素的平均值和标准差。
// 可以使用数组方法map()和reduce()来实现同样的计算,并且这种实现极其简洁
// 首先定义两个简单的函数
var sum = function(x,y){return x + y;};
var square = function(x){return x*x;};
// 然后将这些函数和数组方法配合使用计算出平均数和标准差
var data = [1,1,3,5,5];
var mean = data.reduce(sum)/data.length;
var deviations = data.map(function(x){return x-mean;});
var stddev = Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1));
// 高阶函数:就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数。
// 这个高阶函数返回一个新的函数,这个新函数将它的实参传入f()
// 并返回f的返回值的逻辑非
function not(f){
return function(){ // 返回一个新的函数
var result = f.apply(this,arguments);// 调用f()
return !result; // 对结果求反
};
}
var even = function(x){// 判断a是否为偶数的函数
return x%2 === 0;
};
var odd = not(even); // 一个新函数,所做的事情和even()相反
[1,,1,3,5,5].every(odd);// true:每个元素都是奇数
// 实现一个能表示值的范围的类
// 这个工厂方法返回一个新的“范围对象”
function range(from,to){
// 使用inherit()函数来创建对象,这个对象继承自在下面定义的原型对象
// 原型对象作为函数的一个属性存储,并定义所有“范围对象”所共享的方法(行为)
var r = inherit(range.methods);
// 存储新的“范围对象”的起始位置和结束位置(状态)
// 这两个属性是不可继承的,每个对象都拥有唯一的属性
r.from = from;
r.to = to;
// 返回这个新创建的对象
return r;
}
// 原型对象定义方法,这些方法为每个范围对象所继承
range.methods = {
// 如果x在范围内,则返回true,否则返回false
// 这个方法可以比较数字范围,也可以比较字符串和日期范围
includes: function(x){
return this.from <= x && x <= this.to;
},
// 对于范围内的每个整数都调用一次f
// 这个方法只可用作数字范围
foreach: function(f){
for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);
},
// 返回表示这个范围的字符串
toString: function(){return "(" + this.from + "..." + this.to + ")";}
};
// 这里是使用“范围对象”的一些例子
var r = range(1,3); // 创建一个范围对象
r.includes(2); // true:2在这个范围内
r.foreach(console.log);// 输出1 2 3
console.log(r.toString()); // 输出(1...3)
// 使用构造函数来定义“范围类”
// 这是一个构造函数,用以初始化新创建的“范围对象”
// 注意,这里并没有创建并返回一个对象,仅仅是初始化
function Range(from,to){
// 存储“范围对象”的起始位置和结束位置(状态)
// 这两个属性是不可继承的,每个对象都拥有唯一的属性
this.from = from;
this.to = to;
}
// 所有的“范围对象”都继承自这个对象
// 注意,属性的名字必须是“prototype”
Range.prototype = {
// 如果x在范围内,则返回true,否则返回false
// 这个方法可以比较数字范围,也可以比较字符串和日期范围
includes: function(x){ return this.from <= x && x <= this.to;},
// 对于范围内的每个整数都调用一次f
// 这个方法只可用于数字范围
foreach: function(f){
for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);
},
// 返回表示这个范围的字符串
toString: function(){ return "(" + this.from + "..." + this.to + ")";}
};
// 这里是使用“范围对象”的一些例子
var r = range(1,3); // 创建一个范围对象
r.includes(2); // true:2在这个范围内
r.foreach(console.log);// 输出1 2 3
console.log(r.toString()); // 输出(1...3)
// 原型对象是类的唯一标识:当且仅当两个对象继承自同一个原型对象时,它们才是属于同一个类的实例。而初始化对象的状态的构造
// 函数则不能作为类的标识,两个构造函数的prototype属性可能指向同一个原型对象。那么这两个构造函数创建的实例是属于同一个类
// 的。假设这里有一个对象r,检测它是否是Range对象:
r instanceof Range // 如果r继承自Range.prototype,则返回true
// 实际上instanceof运算符并不会检查r是否是由Range()构造函数初始化而来,而会检查r是否继承自Range.prototype。
// 每个JS函数(ES5中的Function.bind()方法返回的函数除外)都自动拥有一个prototype属性。这个属性的值是一个对象,这个对象包含
// 唯一一个不可枚举属性constructor。constructor属性的值是一个函数对象:
var F = function(){}; // 这是一个函数对象
var p = F.prototype; // 这是F相关联的原型对象
var c = p.constructor; // 这是与原型相关联的函数
c === F // true:对于任意函数F.prototype.constructor == F
// 可以看到构造函数的原型中存在预先定义好的constructor属性,这意味着对象通常继承的constructor均指代它们的构造函数。
// 由于构造函数是类的“公共标识”,因此这个constructor属性为对象提供了类。
var o = new F(); // 创建类F的一个对象
o.constructor === F;// true,constructor属性指代这个类
// 上面的Range类使用它自身的一个新对象重写预定义的Range.prototype对象。这个新定义的原型对象不含有constructor属性。因此
// Range类的实例也不含有constructor属性。我们可以通过补救措施来修正这个问题,显式给原型添加一个构造函数:
Range.prototype = {
constructor: Range, // 显式设置构造函数反向引用
includes: function(x){ return this.from <= x && x <= this.to;},
foreach: function(f){
for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);
},
toString: function(){ return "(" + this.from + "..." + this.to + ")";}
};
// 另一种常见的解决办法是使用预定义的原型对象,预定义的原型对象包含constructor属性,然后依次给原型对象添加方法:
// 扩展预定义的Range.prototype对象,而不重写之
// 这样就自动创建Range.prototype.constructor属性
Range.prototype.includes = function(x){ return this.from <= x && x <= this.to;};
Range.prototype.foreach = function(f){
for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);};
Range.prototype.toString = function(){ return "(" + this.from + "..." + this.to + ")";};
// 有时候针对复杂的数据,客户端程序员会借助使用非标准的属性。如上所述,可以使用getAttribute()和setAttribute()来读和
// 写非标准属性的值。在HTML5文档中,任意以“data-”为前缀的小写的属性名字都是合法的。这些“数据集属性”将不会对其元素的
// 表现产生影响,它们定义了一种标准的、附加额外数据的方法,并不是在文档合法性上做出让步。
// Web浏览器很擅长解析HTML,通常设置innerHTML效率非常高,甚至在指定的值需要解析时效率也是相当不错。但注意,对innerHTML
// 属性用“+=”操作符重复追加一小段文本通常效率低下,因为它既要序列化又要解析。
// DocumentFragment是一种特殊的Node,它作为其他节点的一个临时的容器。像这样创建一个DocumentFragment:
var frag = document.createDocumentFragment();
// 像Document节点一样,DocumentFragment是独立的,而不是任何其他文档的一部分。它的parentNode总是为null。但类似Element,它
// 可以有任意多的子节点,可以用appendChild()、insertBefore()等方法来操作它们。
// DocumentFragment的特殊之处在于它使得一组节点被当做一个节点看待。
// z-index只对兄弟元素(例如同一个容器的子元素)应用堆叠效果。如果两个元素不是兄弟元素之间的重叠,那么设置它们的z-index属性
// 无法决定哪一个显示在最上面
// CSS3引进了box-sizing属性,默认值是content-box,如果替换为box-sizing:border-box,浏览器将会为那个元素应用IE的盒模型,
// 即width和height属性将包含边框和内边距。当想以百分比形式为元素设置总体尺寸,又想以像素单位指定边框和内边距时,边框
// 盒模型特别有用:
<div style="box-sizing: border-box; width: 50%; padding: 10px; border:solid black 2px;">
// 边框盒模型在未来CSS3中的一个可选方案是使用盒子尺寸的计算值:
<div style="width: calc(50%-12px); padding: 10px; border: solid black 2px;">
// 事件分类:
// 依赖于设备的输入事件:有些事件和特定输入设备直接相关,如鼠标和键盘。包括诸如"mousedown"、"mousemove"、"mouseup"
// "keydown"、"keypress"和"keyup"这样的传统事件类型,也包括像"touchmove"和"gesturechange"这样新的触摸事件类型
// 独立于设备的输入事件:有些输入事件没有直接相关的特定输入设备。例如,click事件表示激活了链接、按钮或其他文档元素,这
// 通常是通过鼠标单击实现,但也能通过键盘或触摸感知设备上的手势来实现。如尚未广泛实现的textinput事件就是一个独立于设备
// 的输入事件,它既能取代按键事件并支持键盘输入,也可以取代剪切和粘贴与手写识别的事件。
// 用户界面事件:该事件是较高级的事件,通常出现在定义Web应用用户界面的HTML表单元素上。包括文本输入域获取键盘焦点的focus
// 事件、用户改变表单元素显示值的change事件和用户单击表单中的“提交”按钮的submit事件。
// 状态变化事件:由网络或浏览器活动触发的事件,用来表示某种生命周期或相关状态的变化。如Window对象上会发生load事件。HTML5
// 历史管理机制会触发popstate事件来响应浏览器的后退按钮。HTML5离线Web应用API包括online和offline事件。
// 向服务器请求的数据准备就绪时,如何利用readystatechange事件得到通知。类似的,用于读取用户选择本地文件的新API使用像
// "loadstart"、"progress"和"loadend"事件来实现I/O过程的异步通知。
// 特定API事件。如拖放API、<video>和<audio>元素定义了一长串像"waiting"、"playing"等事件。
// 计时器和错误处理程序:属于客户端JS异步编程模型的部分,并有相似的事件。
// unload事件和load事件相对,当用户离开当前文档转向其他文档时会触发它。unload事件处理程序可以用户保存用户的状态,但它不能
// 用于取消用户转向其他地方。beforeunload事件和unload类似,但它能提供询问用户是否确定离开当前页面的机会。如果beforeunload
// 的处理程序返回字符串,那么在新页面加载之前,字符串会出现在展示给用户确认的对话框上,这样用户将有机会取消其跳转而留在
// 当前页面上。
// 对象通过引用来传递,它们永远不会被复制:
var x = stooge;
x.nickname = 'Curly';
var nick = stooge.nickname;//因为x和stooge是指向同一个对象的引用,所以nick为‘Curly’
var a = {}, b = {}, c = {};// a、b、c每个都引用一个不同的空对象
a = b = c = {};// a、b、c都引用同一个空对象
// 每个对象都连接到一个原型对象,并且它可以从中继承属性。所有通过对象字面量创建的对象都连接到Object.prototype,它是JS
// 中的标配对象。
// 当你创建一个新对象时,你可以选择某个对象作为他的原型。
// Object的create方法创建一个使用原对象作为其原型的新对象。
// 原型连接在更新时是不起作用的。
// 原型关系是一种动态的关系。如果我们添加一个新的属性到原型中,该属性会立即对所有基于该原型创建的对象可见。