在typeof的输出类型中,function和object都是对象,为何却要输出两种答案呢?都叫做object不行吗?——当然不行。
console.log(typeof function () {}); //function
1.对象都是通过函数来创建的。
2. 而函数又是另一种对象,
所以有种鸡生蛋蛋生鸡的感觉。 函数与对象。
3. 每个对象(除了null)都有一个隐藏的属性——“__proto__” (“隐式原型”)
//这个属性引用了创建这个对象的函数的prototype。即:fn.__proto__ === Fn.prototype //Object.prototype确实一个特例——它的__proto__指向的是null
每个函数都有一个属性叫做 prototype (['protə'taɪp]原型),其属性值是一个对象(对象是属性的集合)
这个对象默认只有一个 constructor的属性,指向这个函数本身。
5. Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断队则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。 instanceof表示的就是一种继承关系,或者原型链的结构
6. 访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
function Foo(){};
var f1=new Foo();
f1.a=10;
Foo.prototype.a=100;
Foo.prototype.b=200;
f1.a //10
f1.b //200
那么我们在实际应用中如何区分一个属性到底是基本的还是从原型中找到的呢?大家可能都知道答案了——hasOwnProperty,特别是在for…in…循环中,一定要注意
function Foo(){};
var f1=new Foo();
f1.a=10;
Foo.prototype.a=100;
Foo.prototype.b=200;
var item;
for(item in f1){
alert(item); //a b
}
function Foo(){};
var f1=new Foo();
f1.a=10;
Foo.prototype.a=100;
Foo.prototype.b=200;
var item;
for(item in f1){
if(f1.hasOwnProperty(item)){
console.log(item); //只有a
}
}
注意: f1的这个hasOwnProperty方法是从哪里来的? f1本身没有,再沿着__proto__这条链向上找,发现Foo.prototype中也没有,哪儿来的?
Foo.prototype.__proto__又指向 Object.prototype,而Object.prototype中有这个方法。
由于所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这就是所谓的“继承”。
即通过原型链实现继承,
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
7.
console.log(a)
VM1672:1 Uncaught ReferenceError: a is not defined
console.log(a);
var a;
//undefined
console.log(a);
var a=10;
//undefined
在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。可用下图模拟:
var a; //准备工作,
console.log(a);
a=10;
--------补充知识---
function f1(){} //这是函数声明
var f2=function(){} //这是函数表达式,类似 var a=10;
------------------
8.
准备工作会做什么内容?
变量、函数表达式——变量声明,默认赋值为undefined;
this——赋值;
函数声明——赋值;
这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。
所以:
console.log(f1);//会有值
function f1(){};
console.log(f2);//undefined
var f2=function(){};
console.log(this); //任何情况下都有值
其实,javascript在执行一个代码段之前,都会进行这些“准备工作”来生成执行上下文
这个“代码段”其实分三种情况——全局代码,函数体,eval代码。上述讨论的是全局代码
函数体是代码段是因为函数在创建时,本质上是 new Function(…) 得来的,其中需要传入一个文本形式的参数作为函数体
例如:
function fn(x){
console.log(x+5);
}
等同于: var fn = new Function("x","console.log(x+5)");
9.
给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空
如果代码段是函数体,那么在此基础上需要附加:
参数:赋值
arguments:赋值
自由变量的取值作用域:赋值
var a=10;
function fn(){
console.log(a);
}
function bar(f){
var a=20;
f(); //打印10
}
bar(fn);
-----------------------
var a=10;
function fn(){
console.log(a);
console.log(b);
}
function bar(f){
var b=400;
var a=20;
f(); //先打印出来10,接着打印b的值时会报错。
}
bar(fn);
10.在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。
其实我个人认为this的值,可以这么来看:
obj.fn() :带上()表示函数执行,执行时,fn()前边有obj,所以this就代表obj
如果fn(),则fn中的this指的就是window
类似于成员方法和静态方法吧,直接调用fn,等于是静态方法,或者说是window的成员方法
this指向调用该函数的对象
11.执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。
其实这是一个压栈出栈的过程——执行上下文栈。
12. javascript除了全局作用域之外,只有函数可以创建的作用域
13.自由变量: 在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量;
要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”, 这个一步步找的过程,就是作用域链
var x=10;
function fn(){
console.log(x);
}
function show(f){
var x=20;
(function (){
f(); //输出是10,而不是20. 不管在哪里调用这个fn,fn都是在全局作用域中创建的,所以就去那个作用域中取x的值。
})();
}
show(fn); //在fn函数中,取自由变量x的值时,要到哪个作用域中取?——要到创建fn函数的那个作用域中取——无论fn函数将在哪里调用
VM2152:3 10
14.闭包:只需要知道应用的两种情况即可——函数作为返回值,函数作为参数传递。
有些情况下,函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。
function fn(){
var max = 10;
return function bar(x){
if(x>max){
console.log(x);
}
};
}
var f1=fn(); //执行完这一行时,fn()调用完成。按理说应该销毁掉fn()的执行上下文环境,但是这里不能这么做因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是///,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。因此,这里的fn()上下文环境不能被//销毁,还依然存在与执行上下文栈中。即执行到第18行时,全局上下文环境将变为活动状态,但是fn()上下文环境依然会在执行上下文栈中。另外,执行完第18行,全局上下文环境中的max被赋值为100
max=100;
f1(15);// 15