call和apply能指定函数的上下文
function sum() {
alert(this.chinese + this.math + this.english);
}
var xiaoming = {
chinese: 80,
math: 95,
english: 93
};
sum. call(xiaoming);
sum. apply( xiaoming);
函数.call(上下文);
函数.apply(上下文);
call和apply的区别
function sum(b1, b2) {
alert(this.c + this.m + this.e + b1 + b2);
}
sum.call(xiaoming, 5, 3);//call要用逗号罗列参数
sum.apply(xiaoming, [5, 3]);//apply要把参数写到数组中
到底使用call还是apply?
function fun1() {
fun2.apply(this, arguments);
}
function fun2(a, b) {
alert(a + b);
}
fun1(33, 44);
上下文规则总结
规则 | 上下文 |
---|---|
对象.函数() | 对象 |
函数() | window |
数组下标 | 数组 |
IIFE | window |
定时器 | window |
DOM事件处理函数 | 绑定DOM的元素 |
call和apply | 任意指定 |
用new操作符调用函数
- 现在,我们学习一种新的函数调用方式:
new 函数()
- 你可能知道new操作符和“面向对象”息息相关,但是现在,我们先不探讨它的“面向对象”意义,而是先把用new调用函数的执行步骤和它上下文弄清楚
用new操作符调用函数
- JS规定,使用new操作符调用函数会进行“四步走”:
1)函数体内会自动创建出一个空白对象
2)函数的上下文(this)会指向这个对象
3)函数体内的语句会执行
4)函数会自动返回上下文对象,即使函数没有return语句
function fun() {
this.a = 3;
this.b = 5;
}
var obj = new fun();
console.log(obj);
- 第1步:函数体内会自动创建出一个空白对象
第2步:函数的上下文(this)会指向这个对象
第3步:执行函数体中的语句
第4步:函数会自动返回上下文对象,即使函数没有return语句
什么是构造函数
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var xiaoming = new People('小明', 12, '男');
var xiaohong = new People('小红', 10, '女');
var xiaogang = new People('小刚', 13, '男');
- 用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它
顾名思义,构造函数用来“构造新对象”,它内部的语句将为新对象添加若干属性和方法,完成对象的初始化
构造函数必须用new关键字调用,否则不能正常工作,正因如此,开发者约定构造函数命名时首字母要大写
错误的:一个函数名称首字母大写了,它就是构造函数
- 一定要记住:一个函数是不是构造函数,要看它是否用new调用,而至于名称首字母大写,完全是开发者的习惯约定
如果不用new调用构造函数
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People('小明', 12, '男');
People('小红', 10, '女');
People('小刚', 13, '男');
构造函数中的this不是函数本身
类好比是“蓝图”
-
如同“蓝图”一样,类只描述对象会拥有哪些属性和方法,但是并不具体指明属性的值
-
“狗”是类,“史努比”是实例、“小白”是实例
-
Java、C++等是“面向对象”(object-oriented)语言
JavaScript是“基于对象”(object-based)语言
JavaScript中的构造函数可以类比于OO语言中的“类”,写法的确类似,但和真正OO语言还是有本质不同,在后续课程还将看见JS和其他OO语言完全不同的、特有的原型特性
什么是prototype
- 任何函数都有prototype属性,prototype是英语“原型”的意思
- prototype属性值是个对象,它默认拥constructor属性指回函数
- 普通函数来说的prototype属性没有任何用处,而构造函数的prototype属性非常有用
- 构造函数的prototype属性是它的实例的原型
原型链查找
- JavaScript规定:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找”
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.nationality = '中国';
var xiaoming = new People('小明', 12, '男');
console.log(xiaoming.nationality);
- hasOwnProperty方法可以检查对象是否真正“自己拥有”某属性或者方法
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('age'); // true
xiaoming.hasOwnProperty('sex'); // true
xiaoming.hasOwnProperty('nationality'); // false
- in运算符只能检查某个属性或方法是否可以被对象访问,不能检查是否是自己的属性或方法
'name' in xiaoming // true
'age' in xiaoming // true
'sex' in xiaoming // true
'nationality' in xiaoming // true
- 把方法直接添加到实例身上的缺点:每个实例和每个实例的方法函数都是内存中不同的函数,造成了内存的浪费
- 解决办法:将方法写到prototype上
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.sayHello = function () {
console.log('我是' + this.name);
};
People.prototype.sleep = function () {
console.log(this.name + '开始睡觉,zzzz');
};
面向对象到底是什么
- 面向对象的本质:定义不同的类,让类的实例工作
- 面向对象的优点:程序编写更清晰、代码结构更严密、使代码更健壮更利于维护
- 面向对象经常用到的场合:需要封装和复用性的场合(组件思维)
页面上做一个红绿灯,点击红灯就变黄,点击黄灯就变绿,点击绿灯就变回红灯.如果页面上有100个这样的红绿灯呢?使用面向对象编程,就能以“组件化”的思维解决大量红绿灯互相冲突的问题
- 面向对象编程,最重要的就是编写类
TrafficLight类 - 请思考,红绿灯TrafficLight类有哪些属性和方法呢?
- 属性:自己的当前颜色color、自己的DOM元素dom
- 方法:初始化init()、切换颜色changeColor()、绑定事件bindEvent()
Ball类的属性
属性名 | 意义 |
---|---|
x | 圆心坐标x |
y | 圆心坐标y |
r | 圆半径 |
opacity | 透明度 |
color | 颜色 |
dom | DOM元素 |
Ball类的方法
属性名 | 意义 |
---|---|
init | 初始化 |
update | 更新 |
如何实现多个小球动画
小球实例 小球实例 小球实例 小球实例
- 把每个小球实例都放到同一个数组中
- 只需要使用1个定时器,在每一帧遍历每个小球,调用它们的update方法
包装类
什么是包装类
- Number()、String()和Boolean()分别是数字、字符串、布尔值的“包装类”
- 很多编程语言都有“包装类”的设计,包装类的目的就是为了让基本类型值可以从它们的构造函数的prototype上获得方法
- 请看下面的程序: a、b、c是基本类型值么?它们和普通的数字、字符串、布尔值有什么区别么?
var a = new Number(123);
var b = new String('慕课网');
var c = new Boolean(true);
-
Number()、String()和Boolean()的实例都是object类型,它们的PrimitiveValue属性存储它们的本身值
-
new出来的基本类型值可以正常参与运算
-
包装类的目的就是为了让基本类型值可以从它们的构造函数的prototype上获得方法
-
幂和开方:Math.pow()、Math.sqrt()
-
向上取整和向下取整:Math.ceil()、Math.floor()
-
Math.round()可以将一个数字四舍五入为整数
-
Math.max()可以得到参数列表的最大值
-
Math.min()可以得到参数列表的最小值
-
Math.max()要求参数必须是“罗列出来”,而不能是数组
-
还记得apply方法么?它可以指定函数的上下文,并且以数组的形式传入“零散值”当做函数的参数
-
Math.random()可以得到0~1之间的小数
-
为了得到[a, b]区间内的整数,可以使用这个公式:
Date()日期对象 -
使用new Date()即可得到当前时间的日期对象,它是object类型值
-
使用new Date(2020, 11, 1)即可得到指定日期的日期对象,注意第二个参数表示月份,从0开始算,11表示12月
-
也可以是new Date(‘2020-12-01’)这样的写法
日期对象的常见的方法
方法 | 功能 |
---|---|
getDate() | 得到日期1~31 |
getDay() | 得到星期0~6 |
getMonth() | 得到月份0~11 |
getFullYear() | 得到年份 |
getHours() | 得到小时数0~23 |
getMinutes() | 得到分钟数0~59 |
getSeconds() | 得到秒数0~59 |
时间戳
- 时间戳表示1970年1月1日零点整距离某时刻的毫秒数
- 通过getTime()方法或者Date.parse()函数可以将日期对象变为时间戳
- 通过new Date(时间戳)的写法,可以将时间戳变为日期对象