- 基础知识
- 声明定义
- 匿名函数
- 立即执行函数指函数定义时立即执行
- 函数提升
- 形参实参
- 默认参数
- 函数参数
- arguments 是函数获得到所有参数的集合
- 箭头函数
- 递归调用
- 通过阶乘来体验递归调用
- 累加计算方法
- 递归打印倒三角
- 递归修改课程点击数
- 回调函数
- 展开语法
- 标签函数
- this
- 函数调用
- 方法调用
- 构造函数
- 对象字面量
- 箭头函数
- apply/call/bind
- 原理分析
- apply/call
- 制作显示隐藏面板
- bind() 是将函数绑定到某个对象
- 动态改变元素背景颜色,当然下面的例子也可以使用箭头函数处理
函数是将复用的代码块封装起来的模块,在JS中函数还有其他语言所不具有的特性,接下来我们会详细掌握使用技巧。
声明定义在JS中函数也是对象函数是Function类的创建的实例,下面的例子可以方便理解函数是对象。
let hd = new Function("title", "console.log(title)");
hd('wgchen'); // wgchen
标准语法是使用函数声明来定义函数
function hd(num) {
return ++num;
}
console.log(hd(3));
对象字面量属性函数简写
let user = {
name: null,
getName: function (name) {
return this.name;
},
//简写形式
setName(value) {
this.name = value;
}
}
user.setName('wgchen');
console.log(user.getName()); // wgchen
全局函数会声明在window对象中,这不正确建议使用后面章节的模块处理
console.log(window.screenX); // 1040
当我们定义了 screenX 函数后就覆盖了window.screenX方法
function screenX() {
return "wgchen";
}
console.log(screenX()); // wgchen
使用 let/const
时不会压入window
let hd = function() {
console.log("wgchen");
};
window.hd(); //window.hd is not a function
匿名函数
函数是对象所以可以通过赋值来指向到函数对象的指针,当然指针也可以传递给其他变量,注意后面要以 ;
结束。
下面使用函数表达式将 匿名函数
赋值给变量
let hd = function(num) {
return ++num;
};
console.log(hd instanceof Object); //true
let cms = hd;
console.log(cms(3)); // 4
标准声明的函数优先级更高,解析器会优先提取函数并放在代码树顶端,所以标准声明函数位置不限制,所以下面的代码可以正常执行。
console.log(hd(3));
function hd(num) {
return ++num;
};
标准声明优先级高于赋值声明
console.log(hd(3)); //4
function hd(num) {
return ++num;
}
var hd = function() {
return "hd";
};
程序中使用匿名函数的情况非常普遍
function sum(...args) {
return args.reduce((a, b) => a + b);
}
console.log(sum(1, 2, 3)); // 6
立即执行函数指函数定义时立即执行
可以用来定义私有作用域防止污染全局作用域
"use strict";
(function () {
var web = 'wgchen';
})();
console.log(web); //web is not defined
使用 let/const
有块作用域特性,所以使用以下方式也可以产生私有作用域
{
let web = 'wgchen';
console.log(web); // wgchen
}
console.log(web); // Uncaught ReferenceError: web is not defined
函数提升
函数也会提升到前面,优先级行于 var 变量提高
console.log(hd()); // wgchen
function hd() {
return 'wgchen';
}
变量函数定义不会被提升
console.log(hd()); // wgchen
function hd() {
return 'wgchen';
}
var hd = function () {
return 'blog';
}
形参实参
形参是在函数声明时设置的参数,实参指在调用函数时传递的值。
- 形参数量大于实参时,没有传参的形参值为 undefined
- 实参数量大于形参时,多于的实参将忽略并不会报错
// n1,n2 为形参
function sum(n1, n2) {
return n1+n2;
}
// 参数 2,3 为实参
console.log(sum(2, 3)); //5
当没传递参数时值为undefined
function sum(n1, n2) {
return n1 + n2;
}
console.log(sum(2)); //NaN
默认参数
下面通过计算年平均销售额来体验以往默认参数的处理方式
//total:总价 year:年数
function avg(total, year) {
year = year || 1;
return Math.round(total / year);
}
console.log(avg(2000, 3)); // 667
使用新版本默认参数方式如下
function avg(total, year = 1) {
return Math.round(total / year);
}
console.log(avg(2000, 3));
下面通过排序来体验新版默认参数的处理方式, 下例中当不传递 type 参数时使用默认值 asc。
function sortArray(arr, type = 'asc') {
return arr.sort((a, b) => type == 'asc' ? a - b : b - a);
}
console.log(sortArray([1, 3, 2, 6], 'desc'));
默认参数要放在最后面
//total:价格,discount:折扣,dis:折后折
function sum(total, discount = 0, dis = 0) {
return total * (1 - discount) * (1 - dis);
}
console.log(sum(2000, undefined, 0.3));
函数参数
函数可以做为参数传递,这也是大多数语言都支持的语法规则。
订阅
document.querySelector('button').addEventListener('click', function () {
alert('感谢订阅');
})
函数可以做为参数传递
function filterFun(item) {
return item {
return (total += num);
}, 0);
}
console.log(sum(2, 3, 4, 2, 6)); //17
更建议使用展示语法
function sum(...args) {
return args.reduce((a, b) => a + b);
}
console.log(sum(2, 3, 4, 2, 6)); //17
箭头函数
箭头函数是函数声明的简写形式,在使用递归调用、构造函数、事件处理器时不建议使用箭头函数。
无参数时使用空扩号即可
let sum = () => {
return 1 + 3;
}
console.log(sum()); //4
函数体为单一表达式时不需要 return 返回处理,系统会自动返回表达式计算结果。
let sum = () => 1 + 3;
console.log(sum()); //4
多参数传递与普通声明函数一样使用逗号分隔
let hd = [1, 8, 3, 5].filter((item, index) => {
return item item alert('通过回调函数调用'));
使用回调函数递增计算
let hd = ([1, 2, 3]).map(item => item + 10);
console.log(hd)
展开语法
展示语法或称点语法体现的就是收/放特性,做为值时是放,做为接收变量时是收。
let hd = [1, 2, 3];
let [a, b, c] = [...hd];
console.log(a); //1
console.log(b); //2
console.log(c); //3
[...hd] = [1, 2, 3];
console.log(hd); // [1, 2, 3]
使用展示语法可以替代 arguments 来接收任意数量的参数
function hd(...args) {
console.log(args);
}
hd(1, 2, 3, "wgchen"); // [1, 2, 3, "wgchen"]
也可以用于接收部分参数
function hd(site, ...args) {
console.log(site, args); // wgchen (3) [1, 2, 3]
}
hd("wgchen", 1, 2, 3);
使用 ...
可以接受传入的多个参数合并为数组,下面是使用点语法进行求合计算。
function sum(...params) {
console.log(params);
return params.reduce((pre, cur) => pre + cur);
}
console.log(sum(1, 3, 2, 4));
多个参数时 ...
参数必须放后面,下面计算购物车商品折扣
function sum(discount = 0, ...prices) {
let total = prices.reduce((pre, cur) => pre + cur);
return total * (1 - discount);
}
console.log(sum(0.1, 100, 300, 299));
标签函数
使用函数来解析标签字符串,第一个参数是字符串值的数组,其余的参数为标签变量。
function hd(str, ...values) {
console.log(str); //["站点", "-", "", raw: Array(3)]
console.log(values); // ["wgchen", "blog"]
}
let name = 'wgchen',url = 'blog';
hd `站点${name}-${url}`;
this
调用函数时 this 会隐式传递给函数指函数调用时的关联对象,也称之为函数的上下文。
函数调用全局环境下 this 就是 window 对象的引用
console.log(this == window); //true
使用严格模式时在全局函数内 this 为 undefined
var hd = 'wgchen';
function get() {
"use strict"
return this.hd;
}
console.log(get());
//严格模式将产生错误 Cannot read properties of undefined (reading 'hd')
方法调用
函数为对象的方法时 this 指向该对象
可以使用多种方式创建对象,下面是使用构造函数创建对象。
构造函数函数当被 new 时即为构造函数,一般构造函数中包含属性与方法。函数中的上下文指向到实例对象。
构造函数主要用来生成对象,里面的 this 默认就是指当前对象。
function User() {
this.name = "wgchen";
this.say = function() {
console.log(this); //User {name: "wgchen", say: ƒ}
return this.name;
};
}
let hd = new User();
console.log(hd.say()); // wgchen
对象字面量
- 下例中的 hd 函数不属于对象方法所以指向 window
- show 属于对象方法执向 obj 对象
let obj = {
site: "wgchen",
show() {
console.log(this.site); // wgchen
console.log(`this in show method: ${this}`); //this in show method: [object Object]
function hd() {
console.log(typeof this.site); //undefined
console.log(`this in hd function: ${this}`); //this in hd function: [object Window]
}
hd();
}
};
obj.show();
在方法中使用函数时有些函数可以改变 this 如 forEach,当然也可以使用后面介绍的apply / call / bind
let Lesson = {
site: "wgchen",
lists: ["js", "css", "mysql"],
show() {
return this.lists.map(function(title) {
return `${this.site}-${title}`;
}, this);
}
};
console.log(Lesson.show()); // ['wgchen-js', 'wgchen-css', 'wgchen-mysql']
也可以在父作用域中定义引用 this 的变量
let Lesson = {
site: "wgchen",
lists: ["js", "css", "mysql"],
show() {
const self = this;
return this.lists.map(function(title) {
return `${self.site}-${title}`;
});
}
};
console.log(Lesson.show());
箭头函数
箭头函数没有 this, 也可以理解为箭头函数中的 this 会继承定义函数时的上下文,可以理解为和外层函数指向同一个 this。
如果想使用函数定义时的上下文中的 this,那就使用箭头函数。
下例中的匿名函数的执行环境为全局所以 this 指向 window。
var name = 'wgchen';
var obj = {
name: 'blog',
getName: function () {
return function () {
return this.name;
}
}
}
console.log(obj.getName()()); // 返回window.name的值wgchen
以往解决办法会匿名函数调用处理定义变量,然后在匿名函数中使用。
var name = 'wgchen';
var obj = {
name: 'blog',
getName: function () {
var self = this;
return () => {
return this.name;
}
}
}
console.log(obj.getName()()); //返回window.name的值 blog
使用箭头函数后 this 为定义该函数的上下文,也可以理解为定义时父作用域中的 this
var name = 'wgchen';
var obj = {
name: 'blog',
getName: function () {
return () => {
return this.name;
}
}
}
console.log(obj.getName()()); // blog
事件中使用箭头函数结果不是我们想要的
- 事件函数可理解为对象onclick设置值,所以函数声明时 this 为当前对象
- 但使用箭头函数时 this 为声明函数上下文
下面体验使用普通事件函数时 this 指向元素对象
使用普通函数时 this 为当前DOM对象
wgchen
button
let Dom = {
site: "blog",
bind() {
const button = document.querySelector("button");
button.addEventListener("click", function() {
alert(this.getAttribute("desc"));
});
}
};
Dom.bind();
下面是使用箭头函数时 this 指向上下文对象
button
let Dom = {
site: "blog",
bind() {
const button = document.querySelector("button");
button.addEventListener("click", event => {
alert(this.site + event.target.innerHTML);
});
}
};
Dom.bind();
使用 handleEvent 绑定事件处理器时,this 指向当前对象而不是DOM元素。
button
let Dom = {
site: "blog",
handleEvent: function(event) {
console.log(this);
},
bind() {
const button = document.querySelector("button");
button.addEventListener("click", this);
}
};
Dom.bind();
改变 this 指针,也可以理解为对象借用方法,就现像生活中向邻居借东西一样的事情。
原理分析构造函数中的 this 默认是一个空对象,然后构造函数处理后把这个空对象变得有值。
function User(name) {
this.name = name;
}
let hd = new User("wgchen");
可以改变构造函数中的空对象,即让构造函数 this 指向到另一个对象。
function User(name) {
this.name = name;
}
let wgchen = {};
User.call(wgchen, "blog");
console.log(wgchen.name); // blog
apply/call
call与apply 用于显示的设置函数的上下文,两个方法作用一样都是将对象绑定到this,只是在传递参数上有所不同。
- apply 用数组传参
- call 需要分别传参
- 与 bind 不同 call/apply 会立即执行函数
语法使用介绍
function show(title) {
alert(`${title+this.name}`);
}
let lisi = {
name: '李四'
};
let wangwu = {
name: '王五'
};
show.call(lisi, 'wgchen');
show.apply(wangwu, ['blog']);
使用 call 设置函数上下文
wgchen
button
button
function show() {
alert(this.getAttribute('message'));
}
let bts = document.getElementsByTagName('button');
for (let i = 0; i show.call(bts[i]));
}
找数组中的数值最大值
let arr = [1, 3, 2, 8];
console.log(Math.max(arr)); //NaN
console.log(Math.max.apply(Math, arr)); //8
console.log(Math.max(...arr)); //8
实现构造函数属性继承
"use strict";
function Request() {
this.get = function(params = {}) {
//组合请求参数
let option = Object.keys(params)
.map(i => i + "=" + params[i])
.join("&");
return `获取数据 API:${this.url}?${option}`;
};
}
//文章控制器
function Article() {
this.url = "article/index";
Request.apply(this, []);
}
let hd = new Article();
console.log(
hd.get({
row: 10,
start: 3
})
);
//课程控制器
function Lesson() {
this.url = "lesson/index";
Request.call(this);
}
let js = new Lesson();
console.log(
js.get({
row: 20
})
);
wgchen
* {
padding: 0;
margin: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
dl {
width: 400px;
display: flex;
flex-direction: column;
}
dt {
background: #e67e22;
border-bottom: solid 2px #333;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
dd {
height: 200px;
background: #bdc3c7;
font-size: 5em;
text-align: center;
line-height: 200px;
}
wgchen
1
blog
2
function panel(i) {
let dds = document.querySelectorAll("dd");
dds.forEach(item => item.setAttribute("hidden", "hidden"));
dds[i].removeAttribute("hidden");
}
document.querySelectorAll("dt").forEach((dt, i) => {
dt.addEventListener("click", () => panel.call(null, i));
});
bind() 是将函数绑定到某个对象
比如 a.bind(hd)
可以理解为将 a 函数绑定到 hd 对象上即 hd.a()
。
- 与 call/apply 不同bind不会立即执行
- bind 是复制函数形为会返回新函数
bind是复制函数行为
let a = function() {};
let b = a;
console.log(a === b); //true
// bind是新复制函数
let c = a.bind();
console.log(a == c); //false
绑定参数注意事项
function hd(a, b) {
return this.f + a + b;
}
//使用bind会生成新函数
let newFunc = hd.bind({ f: 1 }, 3);
//1+3+2 参数2赋值给b即 a=3,b=2
console.log(newFunc(2));
在事件中使用bind
wgchen
document.querySelector("button").addEventListener(
"click",
function(event) {
console.log(event.target.innerHTML + this.url); // wgchenblog
}.bind({ url: "blog" })
);
动态改变元素背景颜色,当然下面的例子也可以使用箭头函数处理
wgchen
* {
padding: 0;
margin: 0;
}
body {
height: 100vh;
font-size: 3em;
padding: 30px;
transition: 2s;
display: flex;
justify-content: center;
align-items: center;
background: #34495e;
color: #34495e;
}
https://wgchen.blog.csdn.net
function Color(elem) {
this.elem = elem;
this.colors = ["#74b9ff", "#ffeaa7", "#fab1a0", "#fd79a8"];
this.run = function () {
setInterval(
function () {
let pos = Math.floor(Math.random() * this.colors.length);
this.elem.style.background = this.colors[pos];
}.bind(this),
1000
);
};
}
let obj = new Color(document.body);
obj.run();