您好,登录后才能下订单哦!
一、作用域
js中作用域是指可访问变量,对象,函数的集合,也就是调用它们能生效的代码区块。在js中没有块级作用域,只有全局作用域和函数作用域
1、全局,函数作用域
var a = 10
function f1(){
var b = c = 20;
console.log(a); //10
console.log(c); //20
function f2() {
console.log(b); //20
}f2();
}
f1();
console.log(a); //10
console.log(c); //20
console.log(b); //error
var b = c = 20 是指 var b = c; c = 20
在f1函数中c没使用var声明,所以c为全局变量,b为局部变量,绑定在f1函数下,外部访问不到。
2、模仿块级作用域
没有块级作用域,但是有if(),for()等块语句,在块语句内部定义的变量会保留在它们已经存在的作用域内,举个栗子:
if(true) {
var word = 'hello';
console.log(word); //hello
}
console.log(word); //hello
if()语句存在全局作用域下,所以内部定义的变量存在于全局作用域中,无论在哪都可以访问。
function add(num) {
if(num > 10) {
var num = 10;
console.log(num); //10
}
console.log(num); //10
}
add(11);
console.log(num); //Uncaught ReferenceError: num is not defined
此时if()在add函数中,内部定义的变量存在于add函数的作用域中,只有在add函数和块语句中才可以访问到,外部无法访问。
3、使用自执行的匿名函数包裹块语句构建块作用域,也叫私有作用域
function add(num) {
for(var i = 0; i < num; i++) {
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
console.log(i); //10
}
add(10);
将代码改为
function add(num) {
(function () {
for(var i = 0; i < num; i++) {
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
})()
console.log(i); //Uncaught ReferenceError: i is not defined
}
add(10);
此时变量i只能在for()循环中访问到,在add函数和外部都无法访问,并且在匿名函数中定义的任何变量都会在执行结束时被销毁,所以变量i只能在for()循环中使用。
二、执行上下文
javascript运行的代码环境有三种:
function f1() {
var f1Context = 'f1 context';
function f2() {
var f2Context = 'f2 context';
function f3() {
var f3Context = 'f3 context';
console.log(f3Context);
}
f3();
console.log(f2Context);
}
f2();
console.log(f1Context);
}
f1();
//结果:
//f3 context
//f2 context
//f1 context
全局上下文:拥有f1()
f1()的执行上下文:有变量f1Context和f2()
f2()的执行上下文:有变量f2Context和f3()
f3()的执行上下文:有变量f3Context
ECS:执行环境栈,可以理解为代码执行的土壤,即代码执行的地方
js是单线程,任务都为同步任务的情况下某一时间只能执行一个任务
执行一段代码首先会进入全局上下文中,并将其压入ECS中栈顶
首先执行f1(),为其创建执行上下文,进入到栈顶位置,全局上下文被往下压
f1()中有f2(),再为f2()创建f2()的执行上下文,f2()进入到栈顶位置,f1()被往下压,
依次,最终全局上下文被压入到栈底,f3()的执行上下文在栈顶
f3()执行完后,ECS就会弹出其执行上下文(内部变量随之被销毁),f3()上下文弹出后,f2()上下文来到栈顶,开始执行f2(),依次,最后ECS中只剩下全局上下文,它等到应用程序退出,例如浏览器关闭时销毁。
function foo(i) {
if(i == 3) {
return;
}
foo(i+1);
console.log(i);
}
foo(0);
ECS栈顶为foo(3)的的上下文,直接return弹出后,栈顶变成foo(2)的上下文,执行foo(2),输出2并弹出,执行foo(1),输出1并弹出,执行foo(0),输出0并弹出,关闭浏览器后全局EC弹出,所以结果为2,1,0。
三、原型和原型链
1、对象(普通对象、函数对象)
2、构造函数
//创建构造函数
function Word(words){
this.words = words;
}
Word.prototype = {
alert(){
alert(this.words);
}
}
//创建实例
var w = new Word("hello world");
w.print = function(){
console.log(this.words);
console.log(this); //Person对象
}
w.print(); //hello world
w.alert(); //hello world
print()方法是w实例本身具有的方法,所以w.print()打印hello world;alert()不属于w实例的方法,属于构造函数的方法,w.alert()也会打印hello world,因为实例继承,构造函数原型上的方法。
实例w的隐式原型指向它构造函数的显式原型,指向的意思是恒等于
w.proto === Word.prototype
当调用某种方法或查找某种属性时,首先会在自身调用和查找,如果自身并没有该属性或方法,则会去它的proto属性中调用查找,也就是它构造函数的prototype中调用查找。所以很好理解实例继承构造函数的方法和属性:
w本身没有alert()方法,所以会去Word()的显式原型Word.prototype中调用alert(),即实例继承构造函数的方法。
3、原型和原型链
Function.prototype.a = "a";
Object.prototype.b = "b";
function Person(){}
console.log(Person); //function Person()
let p = new Person();
console.log(p); //Person {} 对象
console.log(p.a); //undefined
console.log(p.b); //b
实例p上面并没有a属性,那么会通过proto向上查找,根据:
p.proto == Person.prototype,然后Person.prototype上也没有a属性,Person.prototype仍然是一个对象,上面仍然具有proto属性,根据:
Person.prototype.proto == Function.prototype //false
Person.prototype.proto == Object.prototype //true
Object.prototype.proto == null //再上一级就是null了
此时,Object.prototype.b = "b",所以p.a是undefined,而p.b是"b",
因为没有定义Object.prototype.a,只定义了Function.prototype.a
总结:
1.查找属性,如果本身没有,则会去proto中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有proto,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined
2.p.proto.constructor == function Person(){}
3.p._proto.proto_== Object.prototype
4.p.proto.proto.proto== Object.prototype.proto == null
5.通过proto__形成原型链而非protrotype
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。