JavaScript 中的难点和重要点,排除只是体系之外的 bug。本篇是学习笔记,记录个人理解。
一、一切皆对象:一切(引用类型)都是对象,对象是属性的集合。
function show(x) { console.log(typeof x); // undefined console.log(typeof 10); // number console.log(typeof 'abc'); // string console.log(typeof true); // boolean console.log(typeof function () {}); //function console.log(typeof [1, 'a', true]); //object console.log(typeof { a: 10, b: 20 }); //object console.log(typeof null); //object console.log(typeof new Number(10)); //object } show();
(undefined, number, string, boolean)属于简单的值类型,不是对象。剩下的(函数、数组、对象、null、new Number(10))就是引用数据类型,属于对象。
附: typeof xxx:判断变量的数据类型; xxx instanceof Array:精确判断变量是否属于某一类型。
var fn = function () { }; console.log(fn instanceof Object); // true
通常我们声明一个 js 对象通常写成类 json(键值对) 格式,但函数和数组也可以这样定义属性吗?——当然不行,但是它可以用另一种形式,总之函数/数组之流,只要是对象,它就是属性的集合。 以下例子:
var fn = function () { alert(100); }; fn.a = 10; fn.b = function () { alert(123); }; fn.c = { name: "王福朋", year: 1988 };
fn(); // 100
fn.b; // function(){alert(123);}
fn.b(); // 123
上段代码中,函数就作为对象被赋值了a、b、c三个属性——很明显,这就是属性的集合吗。
其实,jquery 中的 “$” 就是一个函数。
函数是一种对象,但是:函数和对象间的关系不简单的是父子集的关系!
详见:函 - 对 关系
(1)对象都是通过函数创建的。
function Fn() { this.name = '王福朋'; this.year = 1988; } var fn1 = new Fn();
这是最基本的创建对象的写法。这也是 JS 底层中创建对象的方法。
(2)访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。(__proto__ 在“函 - 对 关系”中有相关解释)。
function Foo () {}; var f1 = new Foo(); f1.a = 10; Foo.prototype.a = 100; Foo.prototype.b = 200; console.log(f1.a); // 10 console.log(f1.b); // 200
二、原型及原型链
在 “一切皆对象中” 的 “函 - 对 关系”中有相应介绍。
补充:原型链的好处:
在Java和C#中,你可以简单的理解class是一个模子,对象就是被这个模子压出来的一批一批月饼(中秋节刚过完)。压个啥样,就得是个啥样,不能随便动,动一动就坏了。
而在javascript中,就没有模子了,月饼被换成了面团,你可以捏成自己想要的样子。
首先,对象属性可以随时改动。
对象或者函数,刚开始new出来之后,可能啥属性都没有。但是你可以这会儿加一个,过一会儿在加两个,非常灵活。
三、执行上下文
在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中包括对变量的声明(而不是赋值。变量赋值是在赋值语句执行的时候进行的。)、this 的赋值、“函数表达式”或“函数声明”。
- 变量、函数表达式。例如:
console.log(a); // undefinded var a = 10; console.log(f1) // undefinded var f1 = function() { };
- this 的赋值(详见 js 中的 this 赋值);
- 函数的声明及赋值;
console.log(f1); // function f1() { } function f1() { };
注意:“函数的声明”和“函数表达式”均发生在“准备工作”时,过程却不一样。
以上的三种的数据准备情况称之为“执行上下文”。通俗来讲,在执行代码前,把简要用到的所有变量都事先拿出来,有的直接赋值,有的先用 undefined 占个坑。
贴两个上下文环境的数据内容:
- 全局代码的上下文环境:
- 函数体中的上下文环境内容在上面的基础上多了如下内容:
还有几点需要注意的是:
1.函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
2.函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。比如:
var a = 10; function fn() { console.log(a); } function bar(f) { var a = 20; f(); console.log(f); } bar(fn); // a = 10 // function fn() { console.log(a); }
3.关于函数的执行。上例中应该可以看出来,函数执行真正的符号是 “()” , 前面的基本等同于是个标识,所以真是 锱铢必较...