JS数据类型【函数】(预解析、作用域、声明提升)

文章详细介绍了JavaScript的基本数据类型和引用数据类型,包括Number、String等,以及函数的创建方式,如函数声明、函数表达式。同时,讨论了函数的作用、作用域(全局与局部)、变量提升、预解析和作用域链。还提到了立即执行函数、arguments对象和构造函数的应用。此外,文章还对比了let和var的声明提升差异,并通过示例解释了块级作用域的重要性。

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

基本数据类型

Number String Boolean undefined null BigInt Symbol

引用数据类型

Object (包含 Array、Function)

函数作用

将重复代码封装,以便下次使用,减少代码冗余

构造函数创建
var fn1= new Function("console.log('调用了函数')")		//函数声明
fn1()								//函数调用
console.log(typeof fn1)
使用关键字创建
function fn2(){
	console.log('调用了函数')
}
fn2()
通过函数表达式方式创建
var fn3 = function(){
	console.log('调用了函数')
}
fn3()

eval(String)

将字符串当函数执行

eval('var num = 123')
eval('console.log(num)')
function test(str){
    alert(str);
}
var a='test';
var b='345';
eval(a+'('+123+')');
eval(a+'(b)');

会存在sql注入问题,因此一般不采用

匿名函数

function(){

}

带参函数

function fn( 形参1,形参2){

}
fn(实参1,实参2)

基本类型数据和引用数据类型数据作为函数参数传递

var a = 10 ,b = 20
function Fn(a,b){
	a=100
	b=200
	console.log(a,b)					//100 200
}
Fn(a,b)
console.log(a,b)					//10 20
var obj = {a:10,b:20}
function Fn(obj){
	obj.a = 100
	obj.b = 200
}
Fn(obj)
console.log(obj.a,obj.b)					//100	 200

上面两个例子Fn()分别传递了基本数据类型和引用数据类型的数据,
其中传递基本数据类型的Fn参数其实就是数据本身,它一旦改变,后面获取到该变量的值也会发生改变,但用于作用域的原因,函数内对该变量的改变,改变不了全局变量。
传递引用数据类型的Fn其实传递的是栈内存中的地址,在函数内部,通过地址改变了存储在堆内存中的数据,因此在函数外部通过该地址读取的数据也发生了改变
(通俗易懂地来说,小张有把钥匙,他把家里打扫得特别干净,然而小明也有一把一样的钥匙,他把房间布局改变了,小张通过他手里的钥匙进入房间,里面的布局能不改变吗?)

立即执行函数

声明立即执行函数
(function(){
	console.log('调用了函数')
})

执行立即执行函数
(function(){
	console.log('调用了函数')
})();											//结束时必须添加分号

带参立即执行函数
(function(形参1,形参2){
	console.log(形参1,形参2)
})(实参1,实参2);

Arguments

函数内部的内置对象(伪数组),用于存储函数相关参数
function Fn(a,b){
console.log(arguments) //Arguments伪数组 [‘1’,2,NaN,callee,length,Symbol]
console.log(arguments.callee) //Fn
console.log(arguments.callee.caller)
}
Fn(‘1’,2,NaN)

arguments.callee

返回当前函数的引用,通俗一点,该argument在哪个函数中,arguments.callee就显示那个函数的函数名
使用场景:递归

arguments.callee.caller

返回当前函数调用者,
使用关键字声明的函数在调用时返回null

function Fn(a,b){
	console.log(arguments.callee.caller)			//null
}
Fn('1',2,NaN)			

构造函数中的方法,实例化的对象,调用方法后arguments.callee.caller返回结果为null

function Product(pname,price){
	this.pname =pname
	this.price=price
	this.show = function(a,b){
		console.log('品名',this.pname,'价格',this.price)
        console.dir(arguments.callee.caller)											//null
	}
}
let p1 = new Product('华为',4000)
p1.show(1,2)

对象的中的方法调用,返回null

let obj = {
    show:function(a,b){
        console.log(arguments.callee.caller)			//null
    }
    
}
obj.show(1,2)

当一个函数被另一个函数调用时,后一个函数的arguments.callee.caller返回前一个函数的函数体

function Fn1(){
    Fn2(1,2)
}
function Fn2(a,b){
    console.log(arguments.callee.caller)			//ƒ Fn1(){	Fn2(1,2)	}
}
Fn1()

什么是伪数组以及怎样判断伪数组、伪数组转为真数组的方法前面已经提到过了

工厂函数

function Product(pname,price){
	this.pname =pname						//this代表当前创造出来的Object
	this.price=price

	this.show = function(){
		console.log('品名',this.pname,'价格',this.price)
	}
}
let p1 = new Product('华为',4000)				//创建对象p1	同时p1也叫Product的实例
let p2 = new Product('小米',3000)				//创建对象p2			p1和p2不是同一个对象!

p1.show()						//p1.show()与p2.show()也不是同一个			当前show的this指向p1
p2.show()																							//当前show的this指向p2

作用域

即一个变量的作用范围

function Fn(){
	var a=0
}
console.log(a)				//报错 caught ReferenceError: a is not defined

全局作用域

在script中在函数外用var、let、const 或直接声明的变量或常量,该变量在所有函数中都能调用
页面开启全局作用域就开启,页面关闭会被浏览器销毁
全局作用域中有一个全局对象window,它的key值包含console、document、alert、prompt、scroll、scrollBy、scrollTo

局部作用域(函数作用域)

函数中声明的变量或常量,只能在该函数内使用

声明提升与预解析

作用域链
var a=1
function Fn1(){
	function Fn2(){
		console.log(a)
	}
}

Fn2找不到a寻找Fn1中的a,而Fn1中没有a,在全局作用域中寻找,寻找变量a所形成的链形结构就叫作用域链

预解析
console.log(a)		//undefined
var a=0
console.log(a)		//0

js代码执行时先预解析(1.检查语法;2.声明提升)再执行代码
当前代码在预解析时,将变量a提前,此时a值为undefined,因此第一句返回undefined
当运行到第二句时,a被赋值将值从undefined变为0
当运行第三句时,输出已经改变了值的变量a

声明提升

console.log(a)			//undefined
if(false){
	var a=10
}
console.log(a)			//undefined

分析

var a
console.log(a)			//undefined
if(false){
	var a=10
}
console.log(a)			//undefined
console.log(Fn)				//ƒ Fn(){ var a=1 console.log(a) }
console.log(Fn())				//1	undefined
function Fn(){
	var a=1
	console.log(a)
}
console.log(Fn())			//1	undefined

js代码执行时先预解析,Fn及其函数体被提前
执行第一句,打印ƒ Fn(){ var a=1 console.log(a) }
执行第二句,返回1,undefined(由于没有return,因此多返回了undefined)
执行第七句,再次调用解析时的Fn,返回1,undefined
由此可以看出,变量的预解析与function声明的函数的声明提升结果是不同的,
函数提升优先于变量提升

console.log(Fn)				//undefined
console.log(Fn())				//报错,后面代码不会执行
let Fn=function(){
	var a=1
	console.log(a)
}
console.log(Fn())			
var A=1
function Fn(){
	console.log(A)
	var A=2
	console.log(A)
	function A(){
		return 111
	}
}
Fn()

它的解析与执行顺序为:

var A									//变量提升
function Fn(){
	console.log(A)
	var A=2
	console.log(A)
	function A(){
		return 111
	}
}
A=1									//执行
//Fn()	执行	预解析Fn()内部变量或者函数			function A(){			//function声明的函数变量提升的优先级高于变量优先级
														return 111
													}
													var A					//重复声明,值延用之前的值
													console.log(A)		//执行		输出ƒ Fn(){ return 111 }
													A=2						///执行
													console.log(A)		//执行		输出2
f1()
console.log(c)
console.log(b)
console.log(a)
function f1(){
	var a=b=c=6
	console.log(a)
	console.log(b)
	console.log(c)
}
分析:
function f1(){					//变量提升
	var a=b=c=6
	console.log(a)
	console.log(b)
	console.log(c)
}
f1()								//执行
					function f1(){					
						var a							//变量提升
						a=b=c=6						//执行					b与c作为全局变量,并赋值为6
						console.log(a)				//执行		读取当前作用域a的值为6
						console.log(b)				//执行		读取全局作用域a的值为6
						console.log(c)				//执行		读取全局作用域a的值为6
					}
console.log(c)			//执行		读取函数f1执行后定义的全局变量c		6
console.log(b)			//执行		读取函数f1执行后定义的全局变量b		6
console.log(a)			//执行		用于变量a在函数f1中声明	作用域只存在于函数f1内	因此全局变量找不到变量a	报错caught ReferenceError: a is not defined
		var num = 123
        function f1(){
            console.log( num );         //2、123
        }
        function f2(){
            console.log( num );         //1、undefined
            var num = 456
            f1()
            console.log( num );         //3、456
        }
        f2()
		(function(a){
            console.log(a);             //100
            var a = 10
            console.log(a);             //10
        })(100)
变量提升的顺序
var声明的变量与function声明的函数
按照顺序进行预解析,但需要注意:var在function前,先得到的undefined被function覆盖,function在var前,先得到function,但后面的undefined会变为先前得到的function
立即执行函数形参与var
先把立即执行函数形参赋值,解析var的同名变量得到undefined,但会变为赋值的参数(实参)
立即执行函数形参与function
先把立即执行函数形参赋值,解析function的同名函数得到函数体,该函数体会替换先前赋值的实参
		(function(a){                   //100       f a(){ console.log(a); }(100被替换)
            console.log(a);             //f a(){ console.log(a); }
            function a(){
                console.log(a);         
            }   
            console.log(a);             //f a(){ console.log(a); }
            a()                         //f a(){ console.log(a); }
        })(100)
立即执行函数形参与var function
先把立即执行函数形参赋值,解析var的同名变量得到undefined,但会变为赋值的参数(实参),解析function的同名函数得到函数体,该函数体会替换先前的优胜者(实参)
		(function(a){                   //100     undefined(取100)         f a(){ console.log(a); }(100被替换)
            console.log(a);             //f a(){ console.log(a); }
            var a = 10
            console.log(a);             //10
            function a(){
                console.log(a);         
            }
            a()                         //10()         is not a function
        })(100)
立即执行函数形参与function var
先把立即执行函数形参赋值,解析function的同名函数得到函数体,该函数体会替换先前赋值的实参,解析var的同名变量得到undefined,但会变为先前的优胜者(function的同名函数函数体)
   		(function(a){                   //100        f a(){ console.log(a); }(100被替换)        undefined(取f a(){ console.log(a); })
            console.log(a);             //f a(){ console.log(a); }
            function a(){
                console.log(a);         
            }
            console.log(a);             //f a(){ console.log(a); }
            var a = 10
            a()                         //10()         is not a function
        })(100)
let声明提升与var声明提升
console.log(a)						//undefined
var a=1

console.log(b)						//报错 let不存在声明提升		643:1 Uncaught ReferenceError: b is not defined
let b=1

区别
let(const)不允许重复声明 //caught SyntaxError: Identifier ‘变量名’ has already been declared
let(const)具有块级作用域
const声明必须赋初始值
const声明常量,常量名建议大写
const可以声明对象、数组,不过一般不建议

<body>
	<button>按钮一</button>
	<button>按钮二</button>
	<button>按钮三</button>
	<button>按钮四</button>
</body>
<script>
	const btns = document.querySelector All("button")
	for(var i=0; i<btns.length; i++) {
		btns[i] .onclick=function() {
		alert(i)
	}
</script>
//无论点击哪个都是4
<script>
	const btns = document.querySelector All("button")
	for(let i=1; i<btns.length; i++) {
		btns[i] .onclick=function() {
		alert(i)
	}
</script>
//点击按钮显示对应数值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值