原型和闭包
一切皆对象
一切皆对象(类型值除外)
undefined, number, string, boolean属于简单的值类型
函数、数组、对象、new Number(10)都是对象。他们都是引用类型
Null是基本数据类型,不是引用数据类型
基本数据类型的值就是它本身的值,引用数据类型是存放的对这个对象引用的指针,Null本身的值就是Null,所以不是引用类型
不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判
断为 object 类型, null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“ object ”。
判断值类型的类型用typeof,引用类型的类型用instanceof
javascript为弱类型,里一切皆是对象,对象里面都是属性,而它的方法也是一种属性,用键值对的形式来表示,且javascript中的对象可以随意扩展属性,没有class的约束。
简单示例
var a1={
b:10,
c:function(n){
alert(this.a+n);
},
d:{car:"000000",long:"五十"}
};
虽然函数是一种对象,但函数和对象那个之间更像是一种相互生成的关系
对象都由函数来创建,我们平时所写的var let等其实是一种“语法糖”其本质还是函数
prototype原型
每一个函数都有一个属性叫prototype
prototype的属性值是一个对象,只有一个默认的叫constructor属性,指向函数本身,而还我们可以自己采用自定义的形式在prototype中新增自己的属性
function F1(){}
F1.prototype.age="1982";
F1.prototype.sex=function(){
return "man";
};
而这样做的作用就要联系到jQuery了
var $("div");
$div.attr('myAge','18');
上面代码中,$(‘div’)返回的是一个对象,而对象被函数创建的而他的实现过程如下
myjQuery.prototype.attr=function(){
};
$('div')=new myjQuery();
其本质就是
function F1(){}
F1.prototype.age="1982";
F1.prototype.sex=function(){
return "man";
};
var f2=new F1();
console.log(f2.age);
console.log(f2.sex());
F1是一个函数,f2对象是从F1函数new出来的,这样f2对象就可以调用F1.prototype中的属性。
因为每个对象都有一个隐藏的属性——“proto”,这个属性引用了创建这个对象的函数的prototype。即:
f2.__proto__ === F1.prototype
这里的"proto_"成为“隐式原型”
__ proto__ 原型
每个函数function都有一个prototype,即原型。同时每个对象都有一个__ proto__
其指向创建该对象的函数的prototype
个__ proto __是一个隐藏的属性,javascript不希望开发者用到这个属性值,有的低版本浏览器甚至不支持这个属性值
自定义函数的prototype都是被Object创建,所以它的_ _proto__指向的就是Object.prototype
但是Object.prototype确实一个特例——它的__ proto__指向的是null,切记切记
函数也有原型
function fn(x,y){
return x+y;
};
console.log(fn(10,20));
var f1= new Function("x","y","return x+y;");
console.log(f1(8,7));
第二种为new Function仅作理解使用,Function作为函数,也是一种对象,所以也有 __ proto__ 属性,而函数本身是被Function创建,所以Function是被自身创建,他的 __ proto __指向了自身的Prototype。
同理Function.prototype指向的对象,它的__ proto __也指向Object.prototype
instanceof
instanceof 用于对引用类型的判断
Instanceof的判断队则是:
function Foo(){}
var f1=new Foo();
console.log(f1 instanceof Foo);//true
console.log(f1 instanceof Object);//true
设第一个变量为A,设第二个函数为B
沿着A的__ proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false
将前面的整合为整体如下图,通过此图,可以捋清为何返回值为true了
其实instanceof表示的就是一种继承关系,或者原型链的结构
继承
javascript中的继承是通过原型链来体现的
js是原型继承,C#是类型继承。
原型继承比类型继承更加灵活,但是又不如类型继承可控
function foo(){}
var f1=new foo();
f1.a=10;
Foo.prototype.a=100;
Fpp.prototype.b=200;
console.log(f1.a);//10
console.log(f1.b);//200
f1是Foo函数new出来的对象f1.a是f1对象的基本属性而非。b从Foo.prototype得来
f1.__ proto __指向的是Foo.prototype
**访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__ proto__这条链向上找,这就是原型链 **
访问f1.b时,f1的基本属性中没有b,于是沿着__ proto__找到了Foo.prototype.b
可使用hasOwnProperty区分一个属性是基本属性还是原型
原型的灵活性
对象属性可以随时改动
在对象或函数new出来后可以随时加属性
继承方法不合适也可以随时修改
缺少你所要用的方法时,可以随时去创建
执行上下文
- 变量、函数表达式——变量声明,默认赋值为undefined;
- this——赋值;
- 函数声明——赋值;
这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。
函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数
另外一点不同在于,函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域
大白话理解:在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空
了解了执行上下文环境中的数据信息,你就不用再去死记硬背那些可恶的面试题了
this
在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了,因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。
-
构造函数
所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)
-
函数作为对象的一个属性
如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象
-
函数用call或者apply调用
当一个函数被call和apply调用时,this的值就取传入的对象的值。
-
全局 & 调用普通函数
全局环境下,this永远是window
普通函数在调用时,其中的this也都是window
其实,不仅仅是构造函数的prototype,即便是在整个原型链中,this代表的也都是当前对象的值。
执行上下文栈
执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。
其实这是一个压栈出栈的过程——执行上下文栈
作用域
通常大家认为“javascript没有块级作用域”。所谓“块”,就是大括号“{}”中间的语句
javascript除了全局作用域之外,只有函数可以创建的作用域。
所以,我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式
作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突
作用域在函数定义时就已经确定了。而不是在函数调用时确定
作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。
所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值
自由变量到作用域链
将变量在作用域外声明在作用域中调用的变量为自由变量
要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,无论函数将在哪里调用
闭包
闭包,可理解为:函数作为返回值,函数作为参数传递
有些情况下函数在被调用完后其上下文环境不会被销毁,如返回值唯一个函数,函数的特别之处在于可以创建一个独立的作用域
而作用域却是在函数创建时就确定了。
所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值
自由变量到作用域链
将变量在作用域外声明在作用域中调用的变量为自由变量
要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,无论函数将在哪里调用
闭包
闭包,可理解为:函数作为返回值,函数作为参数传递
有些情况下函数在被调用完后其上下文环境不会被销毁,如返回值唯一个函数,函数的特别之处在于可以创建一个独立的作用域
使用闭包会增加内容开销,只有在所有闭包相关作用域执行完毕后才会销毁