数据类型:
基本数据类型有六种,分别是undefined,null,boolean,string,number,symbol(ES6新增)
复杂类型只有一种:Object(参照MDN)。Array Date 都是对象的一种(面试可能会问)
-
Null类型只有一个值null,表示一个空对象指针,使用typeof操作符检测null会返回object。但是null不是对象,而是一种基本数据类型
根据《你不知道的JS》书写到 null是js中的一个基本数据类型,之所以显示为'object'是因为对象在底层被表示为二进制,在js中二进制前三位都为0会被判断为object类型,null的二进制表示全是0,自然前三位是0,所有typeof null会返回'object' 这是语言层面的bug 知悉!
存储区别:
-
基本数据类型存储在栈内存,存储的是值
-
复杂数据类型的值存储在堆内存,地址(指向真实数据)存储在栈内存,当我们把对象赋值给另外一个变量的时候,赋值的是地址,指向同一块内存,当一个对象改变时,另外一处也会变化
以上代码中,obj是一个自定义的对象,其中a、b、c就是它的属性,而且在c的属性值还是一个对象,它又有name、year两个属性。
那么函数和数组也可以这样定义属性吗?——当然不行
但是它可以用另一种形式,形式不同
总之函数/数组之流,只要是对象,它就是属性的集合。
prototype和__proto__
函数形式,函数还可以通过prototype 增加属性
var fn = function () {
alert(100);
};
fn.a = 10;
fn.b = function () {
alert(123);
};
fn.c = {
name: "王福朋",
year: 1988
};
例如 jquery $.xxx 这个实现方式是prototype 实例化出来的函数属性
函数也是一种对象。他也是属性的集合,你也可以对函数进行自定义属性。
不用等咱们去试验,javascript自己就先做了表率,人家就默认的给函数一个属性——prototype。对,每个函数都有一个属性叫做prototype。这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。
如这位Object大哥,人家的prototype里面,就有好几个其他属性。
函数是一种对象 对象通过函数创建
函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑
{ } 这个——是一种——“快捷方式”,在编程语言中,一般叫做“语法糖”。
每个函数function都有一个prototype,即原型。这里再加一句话——每个对象都有一个__proto__,可成为隐式原型。
这个__proto__是一个隐藏的属性,javascript不希望开发者用到这个属性值
自定义函数的prototype本质上就是和 var obj = {} 是一样的,都是被Object创建,
所以var obj = {} 的 __proto__指向的就是Object.prototype。
Object.prototype是一个特例——它的__proto__指向的是null,切记切记!
原型prototype的用法
最主要的方法就是将属性暴露成公用的,原型的灵活性:对象属性可以随时改动。
对象或者函数,刚开始new出来之后,可能啥属性都没有。但是你可以这会儿加一个,过一会儿在加两个,非常灵活。
new Function()
function 的由来:由new Function() 生成,和 new Object()是一个形式的。
Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以——Function是被自身创建的。所以它的__proto__指向了自身的Prototype。
Instanceof运算符
Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断队则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
原型链
访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
继承
所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这就是所谓的“继承”。
函数自带方法
我们都知道每个函数都有call,apply方法,都有length,arguments,caller等属性。为什么每个函数都有?这肯定是“继承”的。函数由Function函数创建,因此继承的Function.prototype中的方法。还有hasOwnProperty呢?——那是Function.prototype继承自Object.prototype的方法。
js 执行上下文
两个名词——“函数表达式”和“函数声明”。虽然两者都很常用,但是这两者在“准备工作”时,却是两种待遇。
- 变量、函数表达式——变量声明,默认赋值为undefined;
- this——赋值;
- 函数声明——赋值;
javascript在执行一个代码段之前,都会进行这些“准备工作”来生成执行上下文。这个“代码段”其实分三种情况——全局代码,函数体,eval代码。
-
eval不常用,也不推荐大家用 例如: evel("alert(123)")
-
全局代码 (script 标签中的代码)
-
函数体 (函数)
引入一句名句:
任何问题,都要去追根溯源,要知道这个问题是真正出自哪一块知识点,要真正去理解。光靠背诵是没用的。
arguments变量和函数的参数都已经被赋值。从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域
全局代码的上下文环境数据内容为:
普通变量(包括函数表达式),
如: var a = 10;
声明(默认赋值为undefined)
函数声明,
如: function fn() { }
赋值
this
赋值
如果代码段是函数体,那么在此基础上需要附加:
参数
赋值
arguments
赋值
自由变量的取值作用域
赋值
给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。
this
- 函数中this到底取何值,是在函数真正被调用执行的时候确定,函数定义的时候确定不了
- ****构造函数
**构造函数的prototype,和整个原型链中,this代表的都是当前对象的值。
** - ****函数作为对象的一个属性
- ******函数用call或者apply调用
- ********全局 & 调用普通函数
函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。
执行上下文栈
处于活动状态的执行上下文环境只有一个。其实这是一个压栈出栈的过程——执行上下文栈
有很常用的一种情况,无法做到这样干净利落的说销毁就销毁。这种情况就是伟大的——闭包。
要说闭包,得从自由变量和作用域说起。
作用域
javascript除了全局作用域之外,只有函数可以创建的作用域。作用域在函数定义时就已经确定了。而不是在函数调用时确定
我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式。
匿名函数:限制在一个独立的作用域中,而不会与全局作用域或者其他函数作用域的同名变量产生冲突。两种形式:
(function(){ .. }())
(function foo(){ .. })(windows可传参)
作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。
如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。
变量提升
var a = 2 // var a; 和 a = 2;先有蛋(声明)后有鸡(赋值)
自由变量
要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记——其实这就是所谓的“静态作用域”。
“作用域链”过程:(假设a是自由量)
第一步,现在当前作用域查找a,如果有则获取并结束。如果没有则继续;
第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续;
第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;
第四步,跳转到第一步。
闭包
只需要知道应用的两种情况即可——函数作为返回值,函数作为参数传递。
闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。
使用闭包会增加内容开销,现在很明显了吧!
闭包执行到最后才会去执行销毁
闭包和作用域、上下文环境有着密不可分的关系,真的是“想说爱你不容易”!
JSONP原理
ajax 请求受同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。
强调基础,强调理论!理论和实践相结合不是一句空话。
工作之余,切不可忘记充电
推荐去顶部链接进入阅读原文!!!(为啥三个感叹号 强烈推荐呢,话不多说,我上个图)
推荐去上方连接进入阅读原文!!!(为啥三个感叹号 强烈推荐呢,话不多说,我上个图)
推荐去上方连接进入阅读原文!!!(为啥三个感叹号 强烈推荐呢,话不多说,我上个图)