JavaScript函数
函数是这样的一段javascript代码,它只定义一次,点可能被执行或调用任意次。
变量提升
在js代码编译的过程中,js引擎将会将所有的变量拿出来初始化
比如说
console.log(c);var c=1
undefined
undefined
console.log(b);var b=1
undefined
undefined
console.log(a);let a=1
VM144:1 Uncaught ReferenceError: a is not defined
at <anonymous>:1:13
在声明变量声明之前打印此变量就会被js附上默认值undefined
并且这个行为恰恰反应一个道理 变量提升
等到执行到a=1才会重新赋值
什么是函数
特定功能的代码集合
定义函数方式
1.函数声明
// -1).函数的声明(直接通过function 函数名(){...}的形式来定义函数)
foo();// 打印 window foo ...
function foo() {
console.log("window foo ...");
}
foo();// 打印 window foo ...
2.函数表达式
// -2).函数表达式
fun();// 报错 fun is not defined ...
let fun = function() {
console.log("window fun ...");
};
————————————————
为什么会有这个现象呢?
因为foo这种函数的声明的方式创建的函数会存在变量提升,即在编译的时候会先将foo整个函数放到执行环境中去进行初始化,所以foo函数体是先foo()执行的,所以不会报错且能正常执行。
但是fun属于函数表达式方式创建的函数,其变量提升的时候不是提升的整个函数,而是将变量**fun进行初始化即为其开辟空间赋默认值undefined,**带到代码执行到赋值号(=)的时候,才会将其后面的函数初始化并将函数的指针赋值给fun变量,所以函数是后于fun();执行的,所以你先去执行fun()肯定会报错fun is not defined …
但是如果你将执行语句放在函数定义的下面执行就会正常执行,因为此时fun已被真正赋值 …
3构造器声明
let constructor = new Function("num1", "num2", "console.log('ok');console.log('sum:' + (num1 + num2))");
constructor(1, 2);// 打印 ok sum:3
函数调用
函数声明后需要通过调用才能被执行。JavaScript中通常有4种方式来调用函数:
- 作为普通函数;
- 作为对象方法;
- 作为构造函数;
- 通过它们的
call()
和apply()
方法间接调用。
JS作用域
var arg = "a";
console.log(arg);// 打印 a
function foo() {
var arg = "b";
console.log(arg);// 打印 b
}
foo();
function f(){
}
函数嵌套
function hy(a,b){
function sq(x){return x*x}
return Math.part(sq(a)*sq(b))
}
可以访问包含自己的函数参数
函数它也有全局函数和局部函数
函数返回值
首先要注意在JavaScript中不管我们指定还是没有指定函数返回值,函数都会返回一个值
如果是没有指定 undefined
如果指定 返回指定的值
let res_1 = function back1() {
return "1";
};
console.log(res_1());// 打印 1
let res_2 = function back2() {
};
console.log(res_2());// 打印 undefined
注意情况
!function f() {
return console.log(1),
console.log(2)
}()
1,2
函数的参数传递
关键词解释
- 参数(行参:函数中定义的变量;实参:调用函数时传入的参数);
- 调用上下文:普通函数——this(对象调用,this则为方法上级的对象;直接调用,为window)
//exam 2
var obj={
a:1,
b:2,
c:{
a:3,
fn:()=>{
console.log(this.a)
},
fn2:function(){
console.log(this.a)
}
}
}
var a =4
obj.c.fn() //4
obj.c.fn2() //3
// 函数作为对象也可以当做实参传给其它函数
function eat(food) {
return `吃:${food}`;
}
person("苹果", eat);
function person(food, eat) {
console.log(`人${eat(food)}`);// 打印 人吃:苹果
}
注意:如果存在形参,但并没有传递实参怎么办
第一种写法
// ES5及其之前的默认值设置
function defaultValue(x, y) {
if(typeof y === 'undefined' || Number.isNaN(Number(y))) {
y = 2;
}
y = Number(y);
return x + y;
}
console.log(defaultValue(1));
第二种写法
function defaultValue(x, y = 2) {
return x + y;
}
console.log(defaultValue(1));
注意
但是这种函数写法 不存在 函数length 不能加到 length 的长度上
// -1).length属性 -> 函数形参非设置默认值的参数个数
function sleep(arg_1, arg_2, arg_3 = 3) {
console.log(`${arg_1}只羊,${arg_2}只羊,${arg_3}只羊`);// 打印 1只羊,2只羊,3只羊
console.log(`函数参数的长度(不包括设置默认值的参数个数):${sleep.length}`);// 打印 函数参数的长度(不包括设置默认值的参数个数):2
}
sleep(1, 2);
arguments对象
存储当前的函数里面传递进来的实参
是一个类数组用来访问形参
function watch() {
console.log(`我是数组吗?${arguments instanceof Array}`);// 打印 我是数组吗?false
console.log(`arguments的长度:${arguments.length},第一个参数为:${arguments[0]}`);// 打印 arguments的长度:3,第一个参数为:电视剧
}
watch("电视剧", "电影", "动漫");
instanceof 运算符只能用作对象的判断
this
.this谁调用我所在的方法,我就指向谁
总而言之记住一句话:谁调用我所在的方法,我就指向谁!
全局作用域执行,调用者是window
new构造器创建对象执行 调用者就是new的对象
如果是dom的事件绑定 指向的改DOM元素
严格模式下:
// -1).场景一:普通函数
function walk() {
console.log(`walk this指向了:${this}`);// 打印 walk this指向了:undefined
}
walk();
非严格模式:
// -1).场景一:普通函数
function walk() {
console.log(`walk this指向了:${this}`);// 打印 walk this指向了:window
}
walk();
构造函数
function run(address, speed) {
this.address = address;
this.speed = speed;
this.sayThis = function() {
console.log(`run this指向了:${this},this是否指向构造器的外部实例${this === obj},地址:${this.address},速度:${this.speed}`);// 打印 run this指向了:[object Object],this是否指向构造器的外部实例true,地址:承德市,速度:20
}
}
let obj = new run("承德市", 20);
obj.sayThis();
事件绑定
// -3).场景三:事件绑定
let btn = document.getElementsByTagName("button")[0];
btn.addEventListener('click', function() {
console.log(`this指向:${this}`);// 打印 this指向:[object HTMLButtonElement]
console.log("this"+this.value)
}, false);
JavaScript函数重载
实际上JavaScript没有这个函数重载定义
// 四、JavaScript中的函数没有重载
function talk(language) {
console.log(`语言:${language}`);
}
talk("未知");
function talk(dream) {
console.log(`行为:${dream}`);
}
console.log(talk === talk);// 打印 true
// 打印 行为:未知
为什么最上面的talk方法没有被调用呢?
因为在JavaScript中函数的名字是函数在堆中的指针,
两个talk都指向指定堆中的同一个函数对象,且都对它进行修改,所以必定以最后更改的为准,所以会发生覆盖现象,诸如像Java中的重载,每个重载的方法都有自己的空间,而JavaScript中只要函数名重名就会发生覆盖现象,所以JavaScript没有方法重载的概念。
但是可以实现重载的效果
// 但是我们可以借助arguments来模拟实现函数的重载。
function overLoad() {
if(arguments.length === 1 && typeof arguments[0] === 'number') {
console.log(`数字为:${arguments[0]}`);
}else if(arguments.length === 1 && typeof arguments[0] === 'string') {
console.log(`字符串为:${arguments[0]}`);
}else if(arguments.length === 2) {
console.log(`一参是:${arguments[0]},二参是:${arguments[1]}`);
}
}
overLoad(101);// 打印 数字为:101
overLoad("嘻哈");// 打印 字符串为:嘻哈
overLoad(1, "小天才");// 打印 一参是:1,二参是:小天才