javascript原型和闭包理解

原型和闭包

一切皆对象

一切皆对象(类型值除外)

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 用于对引用类型的判断img

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了

img

其实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__这条链向上找,这就是原型链 **

img

访问f1.b时,f1的基本属性中没有b,于是沿着__ proto__找到了Foo.prototype.b

可使用hasOwnProperty区分一个属性是基本属性还是原型

原型的灵活性

对象属性可以随时改动

在对象或函数new出来后可以随时加属性

继承方法不合适也可以随时修改

缺少你所要用的方法时,可以随时去创建

执行上下文

  • 变量、函数表达式——变量声明,默认赋值为undefined;
  • this——赋值;
  • 函数声明——赋值;

这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。

函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数

另外一点不同在于,函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域

大白话理解:在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空

了解了执行上下文环境中的数据信息,你就不用再去死记硬背那些可恶的面试题了

this

​ 在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了,因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

  1. 构造函数

    所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)

  2. 函数作为对象的一个属性

    如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象

  3. 函数用call或者apply调用

    当一个函数被call和apply调用时,this的值就取传入的对象的值。

  4. 全局 & 调用普通函数

    全局环境下,this永远是window

    普通函数在调用时,其中的this也都是window

其实,不仅仅是构造函数的prototype,即便是在整个原型链中,this代表的也都是当前对象的值。

执行上下文栈

​ 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。

其实这是一个压栈出栈的过程——执行上下文栈img

作用域

通常大家认为“javascript没有块级作用域”。所谓“块”,就是大括号“{}”中间的语句

javascript除了全局作用域之外,只有函数可以创建的作用域。

​ 所以,我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式

作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突

作用域在函数定义时就已经确定了。而不是在函数调用时确定

​ 作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。

所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值

自由变量到作用域链

将变量在作用域外声明在作用域中调用的变量为自由变量

要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,无论函数将在哪里调用

闭包

闭包,可理解为:函数作为返回值,函数作为参数传递

有些情况下函数在被调用完后其上下文环境不会被销毁,如返回值唯一个函数,函数的特别之处在于可以创建一个独立的作用域

使用闭包会增加内容开销,只有在所有闭包相关作用域执行完毕后才会销毁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值