Javascript 编译原理 词法作用域 this 对象拷贝 不变性 存在性 遍历for of 类(混合对象) 混入 原型 反射 对象关联 行为委托 对与对象

本文深入探讨JS的编译原理,包括词法作用域、闭包、this绑定规则、对象复制与不变性等核心概念。解析变量存储与查找规则,探讨函数与块作用域的区别,详解模块化编程及ES6模块API的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编译原理 词法作用域 this 对象拷贝 不变性 存在性 遍历for of 类(混合对象) 混入 原型 反射 对象关联 行为委托 对与对象
1、作用域形成的原因:变量存储在哪?如何找到?
2、编译原理:编译:
分词-解析:AST抽象语法树-代码生成:将AST转换为可执行代码。
js的编译大部分发生在执行前几微秒,优化有JIT,延迟编译甚至实施重编译。
1.2.RHS 获取变量的源值 LHS 找到变量的容器(=号和赋值),从而可以对其赋值
1.3 作用域嵌套
作用域:是根据名称查找变量的一套规则。
如果找不到RHS会报错ReferenceError。而LHS查询时,如果在全局作用域找不到,则会创建一个,在非严格模式下。
RHS另外的异常:对非函数进行函数调用;对引用null、undefined的属性,报TypeError异常。

2 词法作用域

欺骗词法:eval
with:可以将一个没有或者有多个属性的对象处理为一个完全隔离的词法作用域。
2.3函数作用域和块作用域,规避冲突。

5 模块

基于模块的函数并不是一个能被稳定识别的模式,只有在运行时才会被考虑进来。ES6模块API更加稳定,不会在运行时改变。
闭包:当函数可以技术并访问所在的词法作用域,即使函数时在当前的词法作用域之外执行,这时就产生了闭包。

附录动态作用域

什么是词法作用域?是一套关于引擎如何寻找变量以及会在何处找到变量的规则。
动态作用域是让作用域作为一个在运行时被动态确定的形式。作用域链基于调用栈。

this

到底this是什么?
绑定规则:
默认绑定、隐士绑定(最后一层影响调用位置,会在传参和引用时发生隐士丢失)、显示绑定、new绑定

硬绑定

function foo(){
	console.log(this.a);
}
var obj={ a:2 };
var bar=function(){ foo.call(obj); }
bar();
setTimeout(bar,100);
bar.call(window);

硬绑定场景,创建一个包裹函数:

function foo(something){
	console.log(this.a,something);
	return this.a + something;
}
var obj={ a:2 };
var bar=function(){
	return foo.apply(obj,arguments);
};
var b=bar(3);
console.log(b);

创建一个i可以重复使用的辅助函数:

function foo(something){
	console.log(this.a,something);
	return this.a+something;
}
function bind(fn,obj){
	return function(){
		return fn.apply(obj,arguments);
};
}
var obj={ a:2 };
var bar=bind(foo,obj);

var b=bar(3);
console.log(b);

apply实现bind

function foo(s){
	console.log(this,s);
return this.a+s;
}
var obj={a:2};
Function.prototype.bind=function(obj){
	var self=this;	
	console.log("bind",this,arguments);
	var args=Array.from(arguments);
return function(){
console.log("return",this,args,arguments);
	return self.apply(obj,args.slice(1));
}
}
var bar=foo.bind(obj);
bar(3);

js new的机制

测试测试绑定和显示的优先级?

function foo(){
	console.log(this.a)
}
var obj1={a:2,foo:foo};
var obj2={a:3,foo:foo};
obj1.foo();
obj2.foo();
obj1.foo.call(obj2);
obj2.foo.call(obj1);

new和隐式绑定?

function foo(something){
	this.a=something;
}
var obj1={};
var bar=foo.bind(obj1);
bar(2);
console.log(obj1.a);
var baz=new bar(3);
console.log(baz.a);
console.log(obj1.a);

MDN的bind实现:

if(!Function.prototype.bind){
	Function.prototype.bind=function(oThis){
	if(typeof this !== "function"){
		throw new TypeError(
			"Function.prototype.bind - what is trying " + 
			"to be bound is not callable"
		);
	}
	var aArgs=Array.prototype.slice.call(arguments,1),
	fToBind=this,
	fNOP=function(){}
	fBound=function(){
		return fToBind.apply(
			(
				this instanceof fNOP &&
				oThis ? this : oThis
			),
			aArgs.concat(
				Array.prototype.slice.call(arguments)
			);
		)
	}	
	fNOP.prototype=this.prototype;
	fBound.prototype=new fNOP();
	return fBound;
}
}

bind的功能之一可以把除了第一个参数之外的其他参数都传给下层函数。

function foo(p1,p2){ this.val=p1+p2; }
var bar=foo.bind(null,"p1");
var baz=new bar("p2");
baz.val;

更安全的this

function foo(a,b){
	console.log("a:"+a+",b:"+b);
}
var Ø=Object.create(null);
foo.apply(Ø,[2,3]);

//使用bind进行柯里化
var bar=foo.bind(Ø,2);
bar(3);

间接引用

function foo(){
	console.log(this.a);
}
var a=2;
var o={a:3,foo:foo};
var p={a:4};
o.foo();
(p.foo=o.foo)();

软绑定:

if(!Function.prototype.softBind){
	Function.prototype.softBind=function(obj){
		var fn=this;
		//捕获所有curried参数
		var curried=[].slice.call(arguments,1);
		var bound=function(){
			return fn.apply(
				(!this || this === (window || global))?
					obj:this,
				curried.concat.apply(curried,arguments)
			);
		};
		bound.prototype=Object.create(fn.prototype);
		return bound;
	};
}

该方法保留了隐式绑定和显示绑定修改this的能力。

function foo(){
	console.log("name: "+this.name);	
}
var obj={name:"obj"},
obj2={name:"obj2"},
obj3={name:"obj3"};
var fooOBJ=foo.softBind(obj);
fooOBJ();//obj
obj2.foo=foo.softBind(obj);
obj2.foo();//obj2
ooOBJ.call(obj3);//obj3
setTimeout(obj2.foo,10);//obj

this词法:

function foo(){
	return (a)=>{ console.log(this.a); }
}
var obj1={a:2};
var obj2={a:3};
var bar=foo.call(obj1);
bar.call(obj2);

箭头函数的绑定无法被修改。

function foo(){
	setTimeout(()=>{ console.log(this.a) },100);
}
var obj={a:2};
foo.call(obj);

对象

内置对象 String Number Boolean Object Function Array Date RegExp Error

复制对象

function anotherFunction(){}
var anotherObject={c:true};
var anotherArray=[];
var myObject={
	a:2,
	b:anotherObject,
	c:anotherArray,
	d:anotherFunction
}
anotherArray.push(anotherObject,myObject);

JSON安全的复制方法:

var newObj=JSON.parse(JSON.stringify(someObj));

浅复制ES6,Object.assign()

var newObj=Object.assign({},myObject);
newObj.a;
newObj.b===anotherObject;
newObj.c===anotherArray;
newObj.d===anotherFunction;

属性描述符

var myObject={a:2};
Object.getOwnPropertyDescriptor(myObject,"a");
{value: 2, writable: true, enumerable: true, configurable: true}
var myObject={};
Object.defineProperty(myObject,"a",{
	value:2,
	writable:true,
	configurable:true,
	enumerable:true
	});
	muObject.a;

Enumerable设置成false 不会出现在枚举中for…in
不变性

//对象常量
var myObject={};
Object.defineProperty(myObject,"FAVORITE_NUMBER",{
	value:42,
	writable:false,
	configurable:false
	})
//禁止扩展
var myObject={a:2};
Object.preventExtensions(myObject);
myObject.b=3;
muObject.b;//undefined
//密封
Object.seal(Obj)
//冻结
Object。freeze()//最高级别的不可变性 并可以深度冻结

[[Put]]/[[Get]]


var myObject={
	get a(){ return 2; }
};
Object.defineProperty(
	myObject,
	"b",
    {
		get:function(){ return this.a*2 },
		enumerable:true
	}
);
console.log(myObject.a,
myObject.b);

存在性

var myObject={a:2};
console.log(
("a" in myObject),//in会检查是否在myObject对象中
("b" in myObject),
myObject.hasOwnProperty("a"),
myObject.hasOwnProperty("b"));
//true false true false

如果对象没有链接到Object.prototype上,可以使用Object.prototype.hasOwnProperty.call(obj,“a”);
枚举性

var myObject={};
Object.defineProperty(
	myObject,
	"a",
    {enumerable:true,value:2}
);
Object.defineProperty(
	myObject,
	"b",
    {enumerable:false,value:3}
);
console.log(myObject.b,("b" in myObject),myObject.hasOwnProperty("b"));
for(var k in myObject){
	console.log(k,myObject[k]);
}

VM961:12 3 true true
VM961:14 a 2

另一种方式可枚举

var myObject={};
Object.defineProperty(
	myObject,
	"a",
    { enumerable:true, value:2 }
	);
Object.defineProperty(
	myObject,
	"b",
    {enumerable:false,value:3}
	);
console.log(
myObject.propertyIsEnumerable("a"),
myObject.propertyIsEnumerable("b"),
Object.keys(myObject),
Object.getOwnPropertyNames(myObject));

VM3093:12 true false ["a"] (2) ["a", "b"]

目前没有方法获取in操作符使用的属性列表,可以递归遍历对象的整条Prototype保存每一层使用Object.keys
遍历 for of

var myArray=[1,2,3];
var it=myArray[Symbol.iterator]();
console.log(
it.next(),it.next(),it.next(),it.next());

可以给对象定义@@iterator

var myObject={a:2,b:3};
Object.defineProperty(myObject,Symbol.iterator,{
	enumerable:false,
	writable:false,
	configurable:true,
	value:function(){
		var o=this;
		var idx=0;
		var ks=Object.keys(o);
		return {
        	next:function(){
				return {
					value:o[ks[idx++]],
					done:(idx > ks.length)
				};
			}
		};
	}
});
var it = myObject[Symbol.iterator]();
console.log(
it.next(),
it.next(),
it.next());
for(var v of myObject){
	console.log(v);
}
// {value: 2, done: false} {value: 3, done: false} {value: undefined, done: true}
// 2
// 3

也可以直接在定义对象事声明var myObject={a:2,[Symbol.iterator]:function(){}}
无限迭代器

var randoms={
	[Symbol.iterator]:function(){
		return {
			next:function(){
				return { value:Math.random() };
			}
		};
	}
};
var randoms_pool=[];
for(var n of randoms){
	randoms_pool.push(n);
	if(randoms_pool.length===100)break;
}

混合对象“类”

类/继承是什么?类/继承描述了一种代码的组织结构形式,一种咋软件中对真实世界中问题领域的建模方法。

显示混入

function mixin(sourceObj,targetObj){
	for(var key in sourceObj){
		if(!(key in targetObj)){
			targetObj[key]=sourceObj[key];
		}
	}
	return targetObj;
}

var Vehicle={
	engines:1,
	ignition:function(){
		console.log("Turning on my engine.");
	},
	drive:function(){
		this.ignition();
		console.log("Steering and moving forward!");
	}
};
var Car=mixin(Vehicle,{
	wheels:4,
	drive:function(){
		Vehicle.drive.call(this);
		console.log("Rolling on all" + this.wheels + " wheels!");
	}
})
///
Car
{wheels: 4, engines: 1, drive: ƒ, ignition: ƒ}
drive: ƒ ()
engines: 1
ignition: ƒ ()
wheels: 4
__proto__: Object

混合复制

function mixin(sourceObj,targetObj){
	for(var key in sourceObj){
		targetObj[key]=sourceObj[key];
	}
	return targetObj;
}
var Vehicle={};
var Car=mixin(Vehicle,{});
mixin({
	wheels:4,
	drive:function(){}
},Car);

寄生继承

function Vehicle(){
	this.engines=1;
}//传统js类
Vehicle.prototype.ignition=function(){
	console.log("Turning on my engine.");
}
Vehicle.prototype.drive=function(){
	this.ignition();
	console.log("Steering and moving forward!");
};
//寄生类
function Car(){
	//car是一个Vehicle类
	var car=new Vehicle();
	//接着对car进行定制
	car.wheels=4;
	//保存到Vehicle::drive()的特殊引用
	var vehDrive=car.drive;

	//重写Vehicle::drive()
	car.drive=function(){
		vehDrive.call(this);
		console.log("Rolling on all " + this.wheels + " wheels!");
	}
	return car;
}
var myCar=new Car();
myCar.drive();
//
Turning on my engine.
Steering and moving forward!
Rolling on all 4 wheels!

隐式混入

var Something={
	cool:function(){
		this.greeting="Hello World";
		this.count=this.count?this.count+1:1;
	}
};
console.log(
Something.cool(),
Something.greeting,
Something.count,);
var Another={
	cool:function(){
		Something.cool.call(this);
	}
};
console.log(
Another.cool(),
Another.greeting,
Another.count);

原型

var anotherObject={a:2};
var myObject=Object.create(anotherObject);
myObject.a;
2

for…in 和 in都会查找原型链

var anotherObject={a:2};
var myObejct=Object.create(anotherObject);
for(var k in myObject){
	console.log("found : " +k);
}
("a" in myObject);
VM20944:4 found : a
true

最终会链向Obejct.prototype
产生隐式屏蔽

var anotherObject={a:2};
var myObject=Object.create(anotherObject);
console.log(
anotherObject.a,
myObject.a,
anotherObject.hasOwnProperty("a"),
myObject.hasOwnProperty("a"));
myObject.a++;
console.log(
anotherObject.a,
myObject.a,
myObject.hasOwnProperty("a"));

VM1184:3 2 2 true false
VM1184:9 2 3 true//会调用[[put]]赋值

Foo.prototype是什么?最直接的解释,这个对象是在调用new Foo()时创建的,最后会被关联到这个Foo点prototype对象上。

function Foo(){}
var a=new Foo();
Object.getPrototypeOf(a)===Foo.prototype;
true

令人迷惑的“构造函数”

function Foo(){}
Foo.prototype.constructor === Foo;//true
var a=new Foo();
a.constructor === Foo;
//添加constructor
function Foo(){}
Foo.prototype={}
Object.defineProperty(Foo.prototype,"constructor",{
	enumerable:false,
	writable:true,
	configurable:true,
	value:Foo
})

原型继承(委托关系)

function Foo(name){ this.name=name; }
Foo.prototype.myName=function(){ return this.name; }
function Bar(name,label){
	Foo.call(this,name);
	this.label=label;
}

Bar.prototype=Object.create(Foo.prototype);
Bar.prototype.myLabel=function(){
	return this.label;
};
var a=new Bar("a","obj a");
a.myName();
a.myLabel();
"obj a"

会有的一些问题:
1.Bar.prototype=Foo.prototype并不会创建一个关联到Bar.prototype的新对象,只是让Bar.prototype直接引用Foo.prototype对象,执行Bar
2.如果使用Object.create,缺点是需要创建一个新对象然后把旧对象抛弃掉,es6加入了Object.setPrototypeOf()

Bar.prototype=Object.create(Foo.prototype)
Object.setPrototypeOf(Bar.prototype,Foo.prototype)

检查实例的继承祖先(委托关系),内省(反射)

function Foo(){}
Foo.prototype.blah=123;
var a =new Foo();
console.log(a instanceof Foo);
VM307:4 true

instanceof 只能处理对象和函数之间的关系,如果想判断两个对象是否通过Prototype关联
通过类的角度使用instanceof判断两个对象的关系

function isRelatedTo(o1,o2){
	function F(){}
	F.prototype=o2;
	return o1 instanceof F;
}
var a={};
var b=Object.create(a);
isRelatedTo(b,a);
true

另一种判断反射的方法Foo.prototype.isPrototypeOf(a)

1 instanceof
2 (Foo.prototype).prototypeOf(a)

判断在a的整条链中是否出现过Foo.prototype
ES5 Object.getPrototypeOf(a) === Foo.prototype;
浏览器支持非标准模式a.__proto__==Foo.prototype

__proto__大致实现

Object.defineProperty(Object.prototype,"__proto__",{
	get:function(){
		return Object.getPrototypeOf(this);
	},
	set:function(o){
		Object.setPrototypeOf(this,o);
		return o;
	}
	})

对象关联

var foo={
	something:function(){ console.log("Tell me something good..."); }
}
var bar=Object.create(foo);
bar.something();
VM11247:2 Tell me something good...

Object.create会创建一个新对象关联到foo,避免new产生prototype和constructor
Object.create创建的对象无法进行委托,不受原型链干扰,适合存储数据
Object.create()的polyfill代码

if(!Object.create){
	Object.create=function(o){
		function F(){}
		F.prototype=o;
		return new F();
	}
}

内部委托,增加代码可理解性

var anotherObject={
	cool:function(){ console.log("cool!"); }
}
var myObject=Object.create(anotherObject);
myObject.doCool=function(){ this.cool(); }
myObject.doCool();
VM13488:2 cool!

行为委托
对象关联

Task={
	setID:function(ID){this.id=ID;},
	outputID:function(){console.log(this.id);}
};
//让XYZ委托Task
XYZ = Object.create(Task);
XYZ.prepareTask=function(ID,Label){
	this.setID(ID);
	this.label=Label;
};
XYZ.outputTaskDetails=function(){
	this.outputID();
	console.log(this.label);
};
XYZ.prepareTask(1,2);

尽量避免相同命名方法(多态)。this.setID仍然会绑定到XYZ,触发this的隐式绑定规则。
禁止互相委托
思维模型的比较

//面向对象(原型)风格
function Foo(who){
	this.me=who;
}
Foo.prototype.identify=function(){
	return "I am " + this.me;
}
function Bar(who){
	Foo.call(this,who);
}
Bar.prototype=Object.create(Foo.prototype);
Bar.prototype.speak=function(){
	alert("Hello, "+this.identify()+".");
}
var b1=new Bar("b1");
var b2=new Bar("b2");

//对象关联风格
Foo={
	init:function(who){ this.me=who; },
	identify:function(){ return "I am " + this.me; }
};
Bar=Object.create(Foo);
Bar.speak=function(){
	alert("Hello, " + this.identify() + ".");
};
var b1=Object.create(Bar);
b1.init("b1");
var b2=Object.create(Bar);
b2.init("b2");
b2.speak();
b1.speak();

类与对象

function Widget(width,height){
	this.width=width||50;
	this.height=height||50;
	this.$elem=null;
}
Widget.prototype.render=function($where){
	if(this.$elem){
		this.$elem.css({
			width:this.width+"px",
			height:this.height+"px"
	}).appendTo($where);
}
};

//子类
function Button(width,height,label){
	//调用super构造函数
	Widget.call(this,width,height);
	this.label=label||"Default";
	this.$elem=$("<button>").text(this.label);
}

//r让Button继承Widget
Button.prototype=Object.create(Widget.prototype);
//重写render
Button.prototype.render=function($where){
	//super调用
	Widget.prototype.render.call(this,$where);
	this.$elem.click(this.onClick.bind(this));
};

Button.prototype.onClick=function(evt){
	console.log("Button" + this.label + "clicked");
}

$(document).ready(function(){
	var $body=$(document.body);
	var btn1=new Button(125,30,"Hello");
	var btn2=new Button(150,40,"World");

	btn1.render($body);
	btn2.render($body);
})

上面的代码出现了丑陋的多态

class Widget{
	constructor(width,height){
		this.width=width||50;
		this.height=height||50;
		this.$elem=null;
	}
	render($where){
		if(this.$elem){
			this.$elem.css({
				width:this.width+"px",
				height:this.height+"px"
			}).appendTo($where);
		}
	}
}
class Button extends Widget{
	constructor(width,height,label){
		super(width,height);
		this.label=label||"Default";
		this.$elem=$("<button>").text(this.label);
	}
	render($where){
		super($where);
		this.$elem.click(this.onClick.bind(this));
	}
	onClick(evt){
		console.log("Button " + this.label + "clicked");
	}
}

委托控件对象


简洁的设计

var Widget={
	init:function(width,height){
		this.width=width||50;
		this.heigh=height||50;
		this.$elem=null;
	},
	insert:function($where){
		if(this.$elem){
			this.$elem.css({
				width:this.width+"px",
				height:this.height+"px"
			}).appendTo($where);
		}
	}
};
var Button=Object.create(Widget);
LoginController.prototype=Object.create(Controller.prototype);
LoginController.prototype.getUser=function(){
	return document.getElementById("login_username").value;
}
LoginController.prototype.getPassword=function(){
	return document.getElementById("login_password").value;
};
LoginController.prototype.validateEntry=function(user,pw){
	user=user || this.getUser();
	pw=pw || this.getPassword;
	if(!(user && pw)){
		return this.failure("Please enter a username & password");
	}
	else if(user.length < 5){
		return this.failure(" Password must be 5+ characters");
	}
	return true;
}

LoginController.prototype.failure=function(err){
	Controller.prototype.failure.call(this,"Login invalid"+err)
}
//子類
function AuthController(login){
	Controller.call(this);
	this.login=login;
}
AurhController.prototype=Object.create(Controller.prototype);
AuthController.prototype.server=function(url,data){
	return $.ajax({url:url,data:data})
}
AuthController.prototype.checkAuth=function(){
	var user=this.login.getUser();
	pw=this.login.getPassword();
	if(this.login.validateEntry(user,pw)){
	this.server("/check-auth",{ user:user,pw:pw })
.then(this.success.bind(this))
.fail(this.failure.bind(this))	
}
};

//重寫基础的success
AuthController.prototype.success=function(){
	Controller.prototype.success.call(this,"Authenticated");
}
AuthController.prototype.failure=function(err){
	Controller.prototype.failure.call(this,"AuthFailed"+err);
}

var auth=new AuthController();
auth.checkAuth(new LoginControler());

自省 鸭子类型
“如果看起来像鸭子,叫起来像鸭子,那就一定是鸭子”

if(a1.something) a1.something();

class陷阱,使用的是 基于[[Prototype]]的实时委托;意外屏蔽问题(会因为id属性和方法屏蔽)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值