js研究(上)

由于私有和特权成员在函数的内部,因此它们会被带到函数的每个实例中。

公有的原型成员是对象蓝图的一部分,适用于通过new关键字实例化的该对象的每个实例

静态成员只适用于对象的一个特殊实例

Function 对象(类)

ECMAScript 最令人感兴趣的可能莫过于函数实际上是功能完整的对象。

Function 类可以表示开发者定义的任何函数。是个函数构造器

构造函数.prototype = 原型对象
原型对象.constructor = 构造函数(模板)

原型对象.isPrototypeof(实例对象) 判断实例对象的原型 是不是当前对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

//父类构造函数sup
function Sup(name){
this.name = name;
}
//父类的原型对象
Sup.prototype = {
constructor : Sup,
sayName : function(){

console.log(this.name);
}
};
//子类构造函数sub
function Sub(age){
this.age = age;
}
//让子类的原型对象 = 父类的实例
//此时的原型对象将包含一个指向另一个原型的指针
//子类的原型对象的构造器变成了父类的构造器
Sub.prototype = new Sup();

元素对象常用属性:

  • obj.innerHTML;
  • obj.outerHTML;
  • obj.innerText;
  • obj.textContent;
  • obj.tagName;
1
2
3
4
5
6
7
8
9
<p>hello world!<span>你好</span></p>
<script>
var p = document.getElementsByTagName('p');//集合
// alert(p[0].textContent);//firefox
// alert(p[0].innerText);//IE
alert(p[0].innerHTML);//hello world!<span>你好</span>
alert(p[0].outerHTML);//<p>hello world!<span>你好</span></p>
alert(p[0].textContent);//hello world!你好
</script>


1
2
3
4
5
6
7
<script>
var forms = document.getElementsByTagName('*');
for(var i=0;i<forms.length;i++){
// alert(forms[i].tagName);//每个元素都有tagName属性
alert(forms[i]);//[object HTMLHtmlElement]等
}
</script>

判断IE与非IE浏览器:

1
2
if(document.all) alert('IE');
else alert("not IE");//用来判断IE与非IE浏览器

setTimeout的定时器值推荐最小使用16.7ms的原因(16.7 = 1000 / 60, 即每秒60帧)。

1
2
3
4
5
6
7
//匿名函数通过引用来调用
fooRef = function () {
};

fooRef();
//没有引用的匿名函数的调用方法
(function () {
})();

标签声明:

JavaScript中的标签就是一个标识符,声明如下:

一个标识符后面跟一个冒号”:”。你可以在任何一个语句前面加上这样的标签。以使得该语句被“标签化”,例如:

1
2
this_is_a_label:
myFunc();

除了单一的语句外,标签也可以用于由大括号表示的符合语句,例如:

1
2
3
4
label_statments: {
var i = 1;
while (i < 10) i++;
}

在JS中有三种方法会涉及“初始化对象实例”的问题:

其一,是通过在构造器中利用this引用来初始化

其二,是通过构造原型实例来初始化

其三,是通过Object.create()并使用属性描述符的方式来构建对象并初始化

当参数表为空与没有参数表是一致的。因此下面两行代码是等意的:

1
2
obj = new constructor;
obj = new constructor();

但是,我们不能认为constructor 后面的括号是函数调用的括号。因为如果这是函数调用的括号,那么下面的代码就应该是合理的了(但事实上,这行代码对于构造器来说是错误的用法):

1
obj = new (constructor());

所以,不能错误地认为:new运算符是“产生对象实例,并调用constructor函数”——尽管看起来是这样

但是,如果我们的确不打算让new后面的函数作为构造器,而只是作为函数使用,可以这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//将foo视为普通函数
function foo(){
var data = this;// 这里暂存了this
return {};
}
obj = new foo();//实例并不是new运算符产生的,而是foo()函数中返回的



var foo = {};
foo.method = function () {
var that = this;
var test = function test() { //store the outer this
console.log(that === foo);//true
};
test();
};
foo.method();


命名函数表达式

//命名函数表达式
var add = function add(a,b){
return a+b;
};

函数表达式

//又名匿名函数
var add = function(a,b){
return a+b;
};

函数的提升(hoisting)

函数声明的行为并不等同于命名函数表达式,其区别在于提升(hoisting)行为,看下面例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script type="text/javascript">
//全局函数
function foo(){alert("global foo!");}
function bar(){alert('global bar');}
function hoist(){
console.log(typeof foo);//function
console.log(typeof bar);//undefined
foo();//local foo!
bar();//TypeError: 'undefined' is not a function
//变量foo以及实现者被提升
function foo(){
alert('local foo!');
}
//仅变量bar被提升,函数实现部分 并未被提升
var bar = function(){
alert('local bar!');
};
}
hoist();
</script>

Call和apply

apply 可以使用数组字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或数组对象, 如 fun.apply(this, new Array('eat', 'bananas'))

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.construct = function (aArgs) {
var oNew = Object.create(this.prototype);//oNew = {}
this.apply(oNew,aArgs);
return oNew;
};
//fun.apply(thisArg[, argsArray])
//thisArg : 如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);

可以通过func1.call(this, arg1, arg2); 或者 func1.apply(this, [arg1, arg2]); 来调用。其中 this 是你想指定的上下文,他可以任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

1
2
3
4
5
6
7
location.hash=anchorname

假设:https://www.google.com.hk/?gws_rd=cr,ssl#newwindow=1&safe=strict&q=location.hash



则其location.hash就是:#newwindow=1&safe=strict&q=location.hash

字面量对象

1
2
3
4
5
6
7
8
9
10
//不管用什么方式调用它(使用new或直接调用),它都会返回一个实例对象:
function Waffle(){
return {
name: 'huang'
};
}
var first = new Waffle(),
second = Waffle();
console.log(first);//{ name: 'huang' }
console.log(second.name);//huang

这种模式的问题是会丢失原型,因此在Waffle()的原型上的成员不会被继承到这些对象中。

Array()构造函数的“陷阱”

我们对new Array()敬而远之还有一个原因,就是为了避免构造函数带来的陷阱。

如果给Array()构造函数传入一个数字,这个数字并不会成为数组的第一个元素,而是设置了数组的长度。也就是说,new Array(3)创建了一个长度为3的数组,而不是某个元素是3。如果你访问数组的任意元素都会得到undefined,因为元素并不存在。

1
2
3
4
5
6
7
8
// 含有1个元素的数组
var a = [3];
console.log(a.length); // 1
console.log(a[0]); // 3
// 含有3个元素的数组
var a = new Array(3);
console.log(a.length); // 3
console.log(typeof a[0]); // "undefined"

JavaScript使用函数来管理作用域,在一个函数内定义的变量称作“本地变量”,本地变量在函数外部是不能被访问的。

任何不通过var声明的变量都会成为全局对象的一个属性

另一种创建全局变量的反模式,就是在var声明中使用链式赋值的方法。在下面这个代码片段中,a是局部变量,但b是全局变量,而作者的意图显然不是这样:

1
2
3
4
5
// 反模式
function foo() {
var a = b = 0;
// ...
}

对象有两种类型的成员:实例成员和原型成员。实例成员直接存在于实例自身,而原型成员则从对象原型继承:

1
2
3
4
var book = {
title: "High Performance Js",
publisher: "Yahoo!Press"
};

此代码中,book对象有两个实例成员:titlepublisher

js给变量赋null值的作用在于:

就是赋值一个空指针,不赋值也可以,只是赋值了让人更容易理解这个变量是用来准备存放对象的,也方便调错。

style.display = "";是清除display样式,display将使用默认值(块元素会变成block,内联元素会变成inline)
style.display="none"; 中“none”是一个值,表示元素将隐藏

querySelector(IE8部分支持)

该方法返回满足条件的单个元素。按照深度优先和先序遍历的原则使用参数提供的CSS选择器在DOM进行查找,返回第一个满足条件的元素。

1
2
element = document.querySelector('div#container');//返回id为container的首个div
element = document.querySelector('.foo,.bar');//返回带有foo或者bar样式类的首个元素

setTimeout() 是属于 window 的 method

1
2
3
4
5
6
7
8
var obj = {
run: function(){
setTimeout(function(){
console.log(this === window);//true
},1000)
}
};
obj.run();

document.readyState == "complete"判断页面是否加载完成

编写高质量代码

1
2
3
4
5
6
7
8
var my = {};//把多个全局变量都追加在一个名称空间下,将显著降低与其他应用程序产生冲突的概率

my.name = {
"first-name": "first",
"last-name": "last",
test: 'hello world'
};
console.log(my.name['first-name']);//字符串形式的属性只能通过[]访问,而不能通过`.`访问

在一个函数中的任何位置定义的变量在该函数中的任何地方都可见。

判断是否是https

1
var isHttps = location.protocol.indexOf('https') > -1;

Location 对象包含有关当前 URL 的信息。

Location 对象属性

属性 描述

1
2
3
4
5
6
7
8
hash	设置或返回从井号 (#) 开始的 URL(锚)。
host 设置或返回主机名和当前 URL 的端口号。
hostname 设置或返回当前 URL 的主机名。
href 设置或返回完整的 URL
pathname 设置或返回当前 URL 的路径部分。
port 设置或返回当前 URL 的端口号。
protocol 设置或返回当前 URL 的协议。
search 设置或返回从问号 (?) 开始的 URL(查询部分)。

Promise
所谓Promise,字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,然后A就可以在写计划的时候这么写:当B返回结果给我的 时候,A执行方案S1,反之如果B因为什么原因没有给到A想要的结果,那么A执行应急方案S2,这样一来,所有的潜在风险都在A的可控范围之内了。

上面这句话,翻译成代码类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var resB = B();
var runA = function() {
resB.then(execS1, execS2);
};
runA();

var resB = B();
var resC = C();
...

var runA = function() {
reqB
.then(resC, execS2)
.then(resD, execS3)
.then(resE, execS4)
...
.then(execS1);
};

runA();

该新特性属于 ECMAScript 2015(ES6)规范:

Promise 对象用来进行延迟(deferred) 和异步(asynchronous ) 计算.。一个 Promise 处于以下四种状态之一:

pending: 初始状态, 非 fulfilled 或 rejected.
fulfilled: 成功的操作.
rejected: 失败的操作.
settled: Promise已被fulfilled或rejected,且不是pending
new Promise(executor);
new Promise(function(resolve, reject) { … });

executor
函数对象,带有两个实参 resolve 和 reject. 第一个参数用来完成(fulfill)当前promise,第二个参数则用来拒绝(reject). 一旦我们的操作完成即可调用这些函数.

promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致

then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由 “等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。

Object和Function都是构造函数,而所有的构造函数的都是Function的实例对象. 因此Object是Function的实例对象

实例对象的constructor([Function: Object])的prototype是对象{}。构造函数的prototype( {} )的constructor是[Function Object]

继承函数的原理莫过于复制类的方法和属性。因此,只要做到这点,就可以实现类的继承了。可以在上面的代码中看见,我们通过遍历prototype来获取原型链中定义的方法和属性。通过apply调用父类的构造器进行构造器中属性和方法的复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function base (d, b, a) {
var p = null, o = d.constructor.prototype, h = {};

for (p in o) {
h[p] = 1;
}
for (p in b.prototype) {
if (!h[p]) {
o[p] = b.prototype[p];
}
}

b.apply(d, a);
}

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function People () {
this.name = "Yorhom";
}

People.prototype.getName = function () {
return this.name;
};

function Student () {
base(this, People, []);
}

var yorhom = new Student();
// "Yorhom"
alert(yorhom.getName());

Each function has two properties: length and prototype

property是DOM中的属性,是JavaScript里的对象;
attribute是HTML标签上的特性,它的值只能够是字符串;

1
Array.prototype.map(function(currentValue,index,array){})

map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值组合起来形成一个新数组。

` Array.prototype.forEach(function(currentVal,idx,array){})

callback 函数会被依次传入三个参数:

数组当前项的值
数组当前项的索引
数组对象本身

函数也是对象,可以往函数上添加属性。将全局变量添加到函数的属性上,可以使之成为局部变量,减少命名冲突问题。