知道函数有原型对象javascript
构造函数,原型对象和实例三者的关系java
原型链面试
会给内置的对象添加自定义的方法数组
会使用更简单的原型使用方式浏览器
####1.1.1 全局做用域函数
整个js执行的环境就是一个全局做用域ui
####1.1.2 局部做用域this
es5规范中: 只有函数才能构成一个局部做用域es5
####1.1.3 做用域链spa
将js执行时变量查找的方式,以链式形式表示出来
var num = 0;
function fn(){
var num1;
num1 = 1;
console.log(num1);
function fnSon(){
var num2 = 2;
console.log(2)
}
}
复制代码
将上面的代码用链式的形式展现出来
词法做用域又叫静态做用域.
做用域是在代码书写完毕以后就造成了,与代码执行无关
内部做用域能够访问外部做用域的变量,可是外部不能够访问内部的
函数的形参就至关于在当前函数的做用域中申明了这个变量
访问变量时,先在本身的做用域中查找,若是没有则沿着做用域链往上找,直到全局.若是全局也没有就报错
给变量赋值以前,要先找变量.查找变量也是沿着做用域链查找,直到全局,若是全局也没有,则会再全局做用域建立这个变量(隐式全局)
代码执行以前先考虑预解析规则,调用函数时,执行函数里的代码以前,函数内也要先执行预解析规则
###面试题:
var a;
if ("a" in window) {
var a = 10;
}
alert(a); //
//=============================================
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
alert(foo); //
}
bar();
//================================================
var num = 123;
function f1(num) {
console.log(num); //
}
function f2() {
var num = 456;
f1(num);
}
f2();
//======================================================
function fn(){
var a = 1, b = 1, c = 1;
}
fn();
console.log(c); //
console.log(b); //
console.log(a); //
function fn1(){
var a = b = c = 1;
}
fn1();
console.log(c); //
console.log(b); //
console.log(a); //
//========================================================
var a = 1;
function fn(){
var a = 2;
function fnSon(a){
a = 3;
console.log(a); //
}
fnSon();
console.log(a); //
}
console.log(a); //
fn();
console.log(a); //
//==========================================================
var a ;
function a(){
console.log('呵呵')
function a(){
a = 4;
console.log('哈哈')
}
a();
console.log(a); //
}
a();
console.log(a); //
//=================================================================
var a = 1;
function a(){
a++;
}
console.log(a) //
//==================================================================
var a = { x : 1 }
var b = a;
a.x = a = { n : 1};
console.log(a.x); //
console.log(b.x); //
//本身把代码运行下,看看和本身想的结果有啥差异
复制代码
###1.3. 建立对象的方式
####1.3.1 简单方式
咱们能够直接经过 new Object()
建立:
var person = new Object()
person.name = 'Jack';
person.age = 18;
person.sayName = function () {
console.log(this.name); //jack
}
复制代码
每次建立经过 new Object()
比较麻烦,因此能够经过它的简写形式对象字面量来建立:
字面量形式的建立方式,底层也是new Object建立出来的
var person = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name);
}
}
复制代码
上面的写法比较简单,可是若是咱们要建立多个person对象呢?
var person1 = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name);
}
}
var person2 = {
name: 'Mike',
age: 16,
sayName: function () {
console.log(this.name);
}
}
var person3 = {
name: 'zs',
age: 17,
sayName: function () {
console.log(this.name);
}
}
复制代码
经过上面的代码咱们不难看出,这样写的代码太过冗余。
####1.3.2 简单方式的改进:工厂函数
咱们能够写一个函数,解决代码重复问题:
function createPerson (name, age) {
return {
name: name,
age: age,
sayName: function () {
console.log(this.name);
}
}
}
复制代码
而后生成对象:
var p1 = createPerson('Jack', 18);
p1.sayName(); //jack
var p2 = createPerson('Mike', 18);
p2.sayName(); // Mike
复制代码
####1.3.3 更优雅的方式(更推荐使用的一种方式):构造函数
一种更优雅的工厂函数就是下面这样,构造函数:
function Person (name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
}
}
var p1 = new Person('Jack', 18);
p1.sayName() // => Jack
var p2 = new Person('Mike', 23);
p2.sayName() // => Mike
复制代码
**构造函数中new关键字作什么事在上一篇博客里写的很详细有须要能够看看
使用构造函数带来的最大的好处就是建立对象更方便了,可是其自己也存在一个浪费内存的问题:
function Person (name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log('hello ' + this.name);
}
}
var p1 = new Person('Tom', 18);
var p2 = new Person('Jack', 16);
复制代码
以上代码的图示:
经过上面的图示,咱们发现,每个对象都引用了一个函数,咱们建立了多少个对象,对应的就会在内存中建立出对应数量的同样的函数.这样形成了内存的极大浪费
function Person (name, age) {
this.name = name;
this.age = age;
this.sayHello = say;
}
function say (){
console.log('hello ' + this.name);
}
var p1 = new Person('Tom', 18);
var p2 = new Person('Jack', 16);
p1.sayHello(); // hello Tom
p2.sayHello(); // hello Jack
复制代码
**注意: ** 这种方式,能够解决构造函数浪费内存的问题,可是,同时又出现了一个新的问题,咱们把函数定义在了全局,
全局的函数,很容易被别人写的代码覆盖.
###3.2 利用函数的原型对象(更优雅的解决方案)
js给每个函数,提供了一个对应的原型对象.能够经过函数的prototype属性访问到这个原型对象.
原型对象有一个constructor的属性会指向本身对应的函数
而咱们经过
new 函数
建立出来的实例对象,默承认以访问到函数对应的原型对象上的属性
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function (){
console.log('hello ' + this.name);
}
var p1 = new Person('Tom', 18);
var p2 = new Person('Jack', 16);
p1.sayHello(); // hello Tom
p2.sayHello(); // hello Jack
复制代码
__proto__
, 经过这个属性,咱们能够在控制台上清楚的看到原型.可是
__proto__
不是w3c标准的属性,因此不要在生产环境(上线)下使用.
function Person (name, age) {
this.name = name;
this.age = age;
}
var p1 = new Person();
console.log(Perosn.prototype === p1.__proto__) //true
复制代码
利用原型对象,能够更加优雅的解决构造函数浪费内存的问题
通常对象私有的属性直接写在构造函数中,而对象公有的属性写在原型对象上
函数对应有一个本身的原型对象 , 经过prototype属性能够访问
原型对象有一个constructor属性,能够指回本身的对应的函数
经过函数new出来的实例,默承认以访问到原型对象的属性 ,咱们能够经过__proto__
在控制台看到
原型对象也是对象,那么这个对象的是被谁建立出来的呢?
function Person (name, age) {
this.name = name;
this.age = age;
}
var p1 = new Person();
console.log(Person.prototype) // 指向Person的原型对象
console.log(Person.prototype.__proto__.constructor) // 咱们能够看到Person的原型对象是Object的实例
复制代码
由于这个查找规则,因此Object函数原型对象上的全部属性均可以被其余对象访问到
function Student(){}
var s1 = new Student();
s1.toString() //[object Object]
复制代码
Array.prototype
String.prototype
...
经过观察内置函数的原型,咱们发现咱们在数组/字符串经常使用的API,其实都定义在他们对应的原型上的.因此全部的数组对象/字符串,都能使用这些API.
##6. 更简单的原型使用方式
若是咱们有不少公用的属性,那么一个一个的添加在函数的原型上就比较麻烦,咱们还能够有一种更简单的方式
直接新建一个对象赋值给函数的prototype属性
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person, // => 手动定义一个 constructor 属性, 指向正确的构造函数
sayHello: function () {
console.log('我叫' + this.name + ',我今年' + this.age + '岁了');
}
eat : function(){
console.log('吃饭ing...');
}
}
var p1 = new Person('zs', 18);
复制代码