此文章为加深对JS中重要概念进行理解,不建议没有任何JS基础的人看,只为加深对概念理解经过实际的例子,而不是看书觉得本身读懂了,可能几天后就忘了,主要是为了理解核心概念,以及对重难点解释。javascript
“一切都是对象”这句话的重点在于如何去理解“对象”这个概念。前端
JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function 是 JS 自带的函数对象。
固然,也不是全部的都是对象,值类型就不是对象。java
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();
以上代码列出了typeof输出的集中类型标识,其中上面的四种(undefined, number, string, boolean)属于简单的值类型,不是对象。剩下的几种状况——函数、数组、对象、null、new Number(10)都是对象。他们都是引用类型。面试
数组是对象,函数是对象,对象仍是对象。编程
对象里面的一切都是属性,只有属性,没有方法数组
那么这样方法如何表示呢?——方法也是一种属性。由于它的属性表示为键值对的形式。
并且,javascript中的对象能够任意的扩展属性,没有class的约束。这个你们应该都知道,就再也不强调了。闭包
先说个最多见的例子:app
var obj = { a: 10, b: function(x) { alert(this.a + x) }, c: { name: "yzh", age: 21 } }
以上代码中,obj是一个自定义的对象,其中a、b、c就是它的属性,并且在c的属性值仍是一个对象,它又有name、year两个属性。编程语言
这个可能比较好理解,那么函数和数组也能够这样定义属性吗?——固然不行,可是它能够用另外一种形式,总之函数/数组之流,只要是对象,它就是属性的集合。函数
var fn = function () { alert(100); }; fn.a = 10; fn.b = function () { alert(123); }; fn.c = { name: "yzh", age: 21 };
上段代码中,函数就做为对象被赋值了a、b、c三个属性——很明显,这就是属性的集合。
(引用类型)都是对象,对象是属性的集合。最须要了解的就是对象的概念。
这块在《JS高级程序设计》也算是大章节下的一块大内容,我只把一些重要的概念写出来让你们理解,具体的深刻要本身去看书中的讲解。
对象都是经过函数建立的
function Fn() { this.name = 'yzh'; this.year = 1996; } var fn1 = new Fn();
有人可能会举出以下反例
var obj = { a: 10, b: 20 }; var arr = [5, 'x', true];
这种作法属于使用“快捷方式”,在编程语言中,通常叫作“语法糖”。
其实以上代码的本质是:
//var obj = { a: 10, b: 20 }; //var arr = [5, 'x', true]; var obj = new Object(); obj.a = 10; obj.b = 20; var arr = new Array(); arr[0] = 5; arr[1] = 'x'; arr[2] = true;
而其中的 Object 和 Array 都是函数:
console.log(typeof (Object)); // function console.log(typeof (Array)); // function
总结:对象都是经过函数来建立的
函数也是一种对象。他也是属性的集合,你也能够对函数进行自定义属性
每建立一个函数,就会同时建立函数的prototype对象。
这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫作constructor的属性,指向这个函数自己。
function Fn() { } Fn.prototype.name = '王福朋'; Fn.prototype.getYear = function () { return 1988; }; var fn = new Fn(); console.log(fn.name); console.log(fn.getYear());
Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就能够调用Fn.prototype中的属性。
由于每一个对象都有一个隐藏的属性——“__proto__”,这个属性引用了建立这个对象的函数的prototype。
即:fn.__proto__ === Fn.prototype
这里的"__proto__"成为“隐式原型”
每一个函数function都有一个prototype,即原型。这里再加一句话——每一个对象都有一个__proto__,可成为隐式原型。__proto__用于指向建立它的构造函数的原型对象
对象 person1 有一个 __proto__属性,建立它的构造函数是 Person,构造函数的原型对象是 Person.prototype ,因此:
person1.__proto__ == Person.prototype
又好比:obj这个对象本质上是被Object函数建立的,所以obj.__proto__=== Object.prototype
在说明“Object.prototype”以前,先说一下自定义函数的prototype。自定义函数的prototype本质上就是和 var obj = {} 是同样的,都是被Object建立,因此它的__proto__指向的就是Object.prototype。
可是Object.prototype确是一个特例——它的__proto__指向的是null.
至于为何简单解释下:
全部的构造器都来自于 Function.prototype,甚至包括根构造器Object及Function自身。全部构造器都继承了·Function.prototype·的属性及方法。如length、call、apply、bind
console.log(typeof Function.prototype) // function console.log(typeof Object.prototype) // object console.log(typeof Number.prototype) // object console.log(typeof Boolean.prototype) // object console.log(typeof String.prototype) // object console.log(typeof Array.prototype) // object console.log(typeof RegExp.prototype) // object console.log(typeof Error.prototype) // object console.log(typeof Date.prototype) // object console.log(typeof Object.prototype) // object
知道了全部构造器(含内置及自定义)的__proto__都是Function.prototype,
Function.prototype的__proto__是谁呢?
console.log(Function.prototype.__proto__ === Object.prototype) // true
这说明全部的构造器也都是一个普通 JS 对象,能够给构造器添加/删除属性等。同时它也继承了Object.prototype上的全部方法:toString、valueOf、hasOwnProperty等。
最后Object.prototype的proto是谁?
Object.prototype.__proto__ === null // true
已经到顶了,为null。
访问一个对象的属性时,先在基本属性中查找,若是没有,再沿着__proto__这条链向上找
javascript中的继承是经过原型链来体现的.
传统原型语法
function Foo() {} Foo.prototype.a = 100; Foo.prototype.b = 200; var f1 = new Foo(); f1.a = 10; alert(f1.a); //10 alert(f1.b); //200
function Foo() {} var f1 = new Foo(); f1.a = 10; Foo.prototype.a = 100; Foo.prototype.b = 200; alert(f1.a); //10 alert(f1.b); //200
对象字面量方法添加属性和方法的注意事项
function Foo() {} Foo.prototype = { a: 100, b: 200 } var f1 = new Foo(); f1.a = 10; alert(f1.a); //10 alert(f1.b); //200
function Foo() {} var f1 = new Foo(); f1.a = 10; Foo.prototype = { a: 100, b: 200 } alert(f1.a); //10 alert(f1.b); //undefined
原型的属性和方法赋值要在,新建实例对象以前,否则没法得到原型的值和属性,alert返回相应的undefined
接上面的例子讲,若是在实例上添加新属性,这个属性就会屏蔽原型对象中保存的同名属性,就是阻止访问了属性,而不是修改原型的属性。
function Foo() {} var f1 = new Foo(); f1.a = 10; Foo.prototype = { a: 100, b: 200 } alert(f1.a); //10 alert(f1.b); //undefined
总结:重写原型对象切断了现有原型与任何以前已经存在的对象实例之间的关系,它们的引用的仍然是最初的原型。
暂时总结到此,有些知识点没有讲到,可能须要你们本身去看书或查阅资料来理解,本人理解也有限,文中如有难以理解的还望大神换个方式来阐述。
后续还有两篇讲解《执行上下文与做用域》和《闭包》,最后一篇闭包可能会有一些前端面试题来说,并在文章末作个总结。