基础复习
1. 变量
变量的作用:存储数据
语法:
声明:var num;
赋值: num = 10;
声明并赋值: var num = 100;
命名规则:
1. 组成:字母、数字、_ $ 不能以数字开头
2. 区分大小写
3. 不能是保留字和关键字
规范:
1. 遵守驼峰式
2. 要有含义
2. 数据类型:
1. 简单数据类型(值类型):string number boolean null undefined
2. 复杂数据类型(引用类型): array object function
3. 类型转换
转换成字符串:var num = 10; String(num); num.toString() num + ""
转换成数字: var str = "10"; parseInt(str) Math.ceil(str) +str
转换成布尔值: var str = "10"; Boolean(str); !!str;
4. 运算符
算术运算符: + - * / %
赋值运算符: = += -= *= /= %=
比较运算符: < <= > >= == === != !==
== === 区别:== 比较值 === 比较值和类型
5 == "5" true
5 === "5" false
逻辑运算符:&& || !
&& || 短路运算符
&& 找假值(值的布尔类型为false: 0 false null undefined NaN "")
|| 找真值
自增自减运算符: ++ --
++num: 先自增,在参与运算
num++:先参与运算,在自增
5. 流程控制语句
顺序结构
分支结构:
if...else if(){} if(){}else if(){} if(){}else if(){}else{}
三元:判断 ? 成立 : 不成立
switch...case
switch(){
case :
break;
case :
break;
default:
break;
};
循环结构
for()
while(条件){循环体}
do{循环体}while(条件) 不管条件是否成立,都会执行一次
6. 数组:有序的键值对集合
创建数组的两种方式:
1. var arr = new Array();
2. var arr = [10, 20, 30];
下标和长度
下标: 每一项数据的唯一标识符,从0开始的,最后一项的下标的:length - 1
长度: length, 数组里面数据的总个数
取值和存值
取值:通过下标来取 arr[0]
存值: arr[0] = 100; arr[3] = 40
循环:
for(var i = 0; i < arr.length; i++){}
7. 函数:复用目的
声明和调用
1. var fn = function(){}
2. function fn2(){}
调用:函数名() fn();
形参和实参
形参: 函数声明的时候写的参数,作用:占位置
实参:函数调用的时候传的参数,作用:给形参赋值
返回值
函数内结果函数需要使用的话,可以返回出去
function sum(n1, n2){
return n1 + n2;
}
var res = sum(); // 返回值
8. 对象 是一个无序的键值对的集合
创建对象的方式
1. var obj = new Object();
2. var obj = {
1: 1000,
name: "xm",
age: 19,
running: function(){}
};
3. 工厂函数
4. 自定义构造函数
属性和方法
属性: 描述对象的特征
方法: 描述对象的行为
操作对象属性的两种方式
1. 点语法 obj.name obj.1
2. 中括号语法 obj["name"] ==> 中括号里面写字符串类型或者是数值 obj[1]
取值和存值
存值:赋值
obj.height = 180;
对象的遍历
for(var k in obj){}
// k是变量 ==> 表示键名
// obj[k] ==> 值
for(var k in obj){
console.log(k, obj[k], obj.k)
obj.name ==> 获取obj对象的name属性的值
obj.k ==> 获取obj对象的k属性的值
}
9. 内置对象
Math
floor ceil random abs ...
Array
push pop ...
Date
一. 课程大纲
1. typeof关键字
typeof操作符返回一个字符串,返回的是操作数的类型
- typeof 基本类型返回的是字符串
- typeof 对象 返回的是object
- typeof 函数 返回的是function
- typeof null 返回的object
1. typeof 在检测简单数据类型的时候,返回的都是对应的类型
例外: typeof null; // object js历史遗留bug
2. typeof 检测复杂数据类型,返回的都是object ==> 万物皆对象
例外:typeof 函数;// function js中函数是一等公民(VIP 特权)
代码演示:
<body>
<script>
// typeof 关键字 检测数据的类型
// 1. typeof 在检测简单数据类型的时候,返回的都是对应的类型
// 例外: typeof null; // object js历史遗留bug
//
// 2. typeof 检测复杂数据类型,返回的都是object ==> 万物皆对象
// 例外:typeof 函数;// function js中函数是一等公民(VIP 特权)
console.log(typeof 1); // number
console.log(typeof "1"); // string
console.log(typeof "true"); // string
console.log(typeof false); // boolean
console.log(typeof null); // object
console.log(typeof undefined); // undefined
console.log(typeof [10, 20]); // object
console.log(typeof new Date()); // object
console.log(typeof console.log); // function
</script>
</body>
2. 逻辑中断
&&:从左到右的顺序进行判断,如果发现某个操作数的逻辑判断是false,那么就不用继续判断了。
- && 运算规则: 从左到右, 找假值(布尔类型为false的值),找到了逻辑就中断了,代码短路了,后续代码就不执行了。返回找到的假值
- 如果没有找到假值,返回最后一个真值
||:从左到右的顺序进行判断,如果发现某个操作数的逻辑是true,那么就不用继续判断了。
- || 运算规则: 从左到右, 找真值(布尔类型为true的值),找到了逻辑就中断了,代码短路了,后续代码就不执行了。返回找到的真值
- 如果没有找到真值,返回最后一个假值
- 使用场景:
- 兼容写法:window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
- 给函数形参设置默认值
代码演示:
<body>
<script>
// 逻辑中断 && || 短路运算符
// 布尔类型为false的值: 0 false null undefined NaN ""
// && 运算规则: 从左到右, 找假值(布尔类型为false的值),找到了逻辑就中断了,代码短路了,后续代码就不执行了。返回找到的假值
// 如果没有找到假值,返回最后一个真值
// console.log( true && false ); // false
// console.log( null && undefined ); // null
// console.log( 1 && [] && {} && "" ); // ""
// console.log( 1 && [] && {} && "abc" ); // "abc"
// 使用场景:
var flag = !true;
/*if(flag){
alert("哈哈");
}*/
// 上面if的另外写法 ==> 短路功能
// flag是false,假值找到了,后续代码不执行。
// flag && alert("哈哈");
// || 运算规则: 从左到右, 找真值(布尔类型为true的值),找到了逻辑就中断了,代码短路了,后续代码就不执行了。返回找到的真值
// 如果没有找到真值,返回最后一个假值
// 使用场景:
// 1. 兼容写法
// window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
// 2. 给函数形参设置默认值
function sum(n){
n = n || 10; // 形参设置默认值 10
console.log(n + 10);
}
sum(20); // 30
sum();
</script>
</body>
3. 值类型与引用类型
值类型:变量在存储的时候,存储的是值得本身
引用类型:变量在存储的时候,存储的是地址
代码演示:
<body>
<script>
// 值类型:变量在存储的时候,存储的是值得本身
// 引用类型:变量在存储的时候,存储的是地址
var num = 10;
var arr = [10, 20, 30]; //数组的地址
// 练习:
var num1 = 10;
var num2 = num1;
num2 = 99;
console.log(num1); //10
console.log(num2); //99
// 2.
var obj1 = {
name:'zs'
}
var obj2 = obj1;
obj2.name = 'ls';
console.log(obj1.name); //ls
console.log(obj2.name); //ls
// 3.
var obj1 = { //obj1存对象的地址
name: 'zs',
}
var obj2 = obj1;
obj1 = {
name: 'ww'
}
obj2.name = 'ls';
console.log(obj1.name);
console.log(obj2.name);
</script>
</body>
值类型与引用类型赋值特征
- 值类型赋值的时候,把值进行赋值
- 引用类型赋值的时候,赋值的是地址。
4. 学习资源
- JavaScript 高级程序设计(第三版)
- 前端的红宝书
- 建议每一个前端都完整的看一遍
- JavaScript面向对象编程指南(第2版)
- JavaScript面向对象精要
- JavaScript 权威指南
- JavaScript 语言精粹
- 你不知道的 JavaScript
二. 对象、原型、原型链
1. 对象
面向对象编程(开发思想)
三大特征:
- 封装性:用对象封装
- 继承性:子承父业
- 多态性:js没有,不支持
创建对象的方式
<body>
<script>
// 面向对象编程(开发思想)
// 三大特征:
// 封装性:用对象封装
// 继承性:子承父业
// 多态性:js没有,不支持
// 创建对象方式
// 1.var obj1 = new Object(); 缺点:麻烦
// var obj1 = new Object();
// obj.name= 'zs';
// console.log(obj1);
// 2.字面量 缺点:每次只能创建一个对象,无法实现批量创建
// var obj2 = {
// name: 'lw',
// age: 38
// };
// console.log(obj2);
// 3. 工厂模式 批量创建
// 使用函数把创建对象的过程进行封装
// 缺点:无法识别对象的具体类型
function createPerson(name, age) {
var obj = {
name: name,
age: age
}
return obj;
}
var xm = createPerson('xm', 21); //xm 接收obj的地址
var xh = createPerson('xh', 22); //xm 接收obj的地址
console.log(xm, xh);
// 4. 自定义构造函数
</script>
</body>
2.自定义构造函数
构造函数的特征:
- 构造函数首字母大写(规范)
- 必须配合 new 一起使用
new 做了四件事:
- 创建新对象
- this 指向创建的新对象
- 执行构造函数内代码
- 返回创建的新对象
术语:
- 实例(对象):构造函数创建的对象叫做实例对象,可以有多个
- 实例化:构造函数创建对象的过程
- 成员:对象的属性和方法的统称
代码演示:
<body>
<script>
function Person(name, age) {
// 在构造函数中通过this.xxx = yyy;形式来给创建的对象添加属性和方法
// this.abc = 'abcd';
this.name = name;
this.age = age;
this.sayHi = function() {
console.log('hello');
}
}
var p = new Person('xm', 20);
var p1 = new Person('xm', 21);
console.log(p);
</script>
</body>
自定义构造函数的缺点
- 自定义构造函数创建对象的缺点:造成了内存浪费
- 每次在执行构造函数的时候,都会在内存中创建功能相同的sayHi方法
代码演示:
<body>
<script>
// 自定义构造函数创建对象的缺点:造成了内存浪费
// 每次在执行构造函数的时候,都会在内存中创建功能相同的sayHi方法
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log('hello');
}
}
var p1 = new Person('xm', 20);
var p2 = new Person('xh', 22);
// console.log(p1, p2);
// 复杂数据类型在比较的时候,比的是内存地址
console.log(p1 === p2); //false
console.log(p1.sayHi === p2.sayHi); //false
</script>
</body>
解决自定义构造函数的缺点
1.把方法放到构造函数外面 ==》 目的为了方法在内存中只有一份 ==> 写多了会造成全局污染
2.把方法方法一个对象中 ==> 可以减少全局污染
代码演示:
<body>
<script>
// 把函数写在构造函数的外面 ===> 确保fn函数在内存中只有一份
// 虽然这种方式可以解决问题,但是会造成全局污染问题
// function fn() {
// console.log('hello');
// }
// function Person(name, age) {
// this.name = name;
// this.age = age;
// this.sayHi = fn; //把fn函数的地址赋值给sayHi方法
// }
// 优化以上代码:
// 1.内存浪费问题要解决
// 2.减少全局污染问题
var obj = {
fn: function () {
console.log('hello');
},
fn2: function () {
console.log('狂吃狂吃');
}
//...
}
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = obj.fn; //把fn函数的地址赋值给sayHi方法
this.eat = obj.fn2; //把fn函数的地址赋值给sayHi方法
}
var p1 = new Person('xm', 23);
var p2 = new Person('xh', 22);
// console.log(p1);
console.log(p1.sayHi === p2.sayHi); //true
console.log(p1.eat === p2.eat); //true
// 解决内存问题:
// 1.把方法放到构造函数外面 ==》 目的为了方法在内存中只有一份 ==> 写多了会造成全局污染
// 2.把方法方法一个对象中 ==> 可以减少全局污染
</script>
</body>
3. 原型
Javascript 规定,每一个函数都有一个 prototype 属性,指向另一个对象。 这个对象的所有属性和方法,都会被构造函数的实例继承。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。
- 任何函数都有prototype属性(侧面也印证了函数是对象)
- 函数的prototype属性值是个对象,把这个对象叫做原型(对象)
- 作用:通过构造函数创建出来的实例对象可以直接去访问这个构造函数的prototype属性上的任意成员。
代码演示:
<body>
<script>
function Person(){
}
// console.dir(Person);
// Person.prototype 原型对象
// 给原型对象添加了color属性,值为lime
Person.prototype.color = "lime";
Person.prototype.legs = 2;
Person.prototype.sayHi = function () {
console.log("hello is me");
}
console.log( Person.prototype );
var p1 = new Person();
// console.log(p1.color); // lime
// console.log(p1.legs); // 2
p1.sayHi();
var p2 = new Person();
p2.sayHi();
// p1和p2所使用的sayHi方法均是来源于Person.prototype原型上
console.log(p1.sayHi === p2.sayHi); // true
// 使用原型解决构造函数创建对象的内存浪费问题
// 原型对象是函数自带的,把sayHi等方法添加到原型对象中
// 作为构造函数创建出来的实例对象可以去访问原型对象上的任意成员
</script>
</body>
构造函数、实例、原型三者之间的关系
构造函数:构造函数就是一个函数,配合new可以新建对象。
实例:通过构造函数实例化出来的对象我们把它叫做构造函数的实例。一个构造函数可以有很多实例。
原型:每一个构造函数都有一个属性prototype,函数的prototype属性值就是原型。通过构造函数创建出来的实例能够直接使用原型上的属性和方法。
代码演示:
<body>
<script>
// 构造函数
function Person() {
}
// Person.prototype 原型对象
Person.prototype.color = "lime";
Person.prototype.sayHi = function() {
console.log("hello is me");
}
// 实例对象
var p1 = new Person();
console.log(p1.color); // lime
p1.sayHi();
var p2 = new Person();
console.log(p2.color);
p2.sayHi();
</script>
</body>
原型思考题
<body>
<script>
// 内置对象的方法都在哪?
// var arr = [10, 20, 30]; // push 方法
// console.log([] === []); // false
// console.log({} === {}); // false
// {} ==> new Object()
// [] ==> 底层还是通过构造函数new出来的
// [] ==> new Array(); // new的第一步:创建新对象,新地址0x11
// [] ==> new Array(); // new的第一步:创建新对象,新地址0x12
// 意味着两个数组使用的push方法是同一个,在内存中只有一份push方法
// 问: 该push方法存在哪?放到数组的原型对象中
// Array.prototype
console.log([].push === [].push); // true
// 这些方法放到原型对象中的好处:
// 1. 在内存中只有一份,解决内存浪费问题
// 2. 只要是实例对象都可以去原型对象上用到原型上的方法
var d = new Date(); // 日期的原型对象 Date.prototype
d.getFullYear()
</script>
</body>
4. _ _proto _ _
任意一个对象,都会有__proto__属性,这个属性指向了构造函数的prototype属性,也就是原型对象。
获取原型对象:
- 通过构造函数.prototype可以获取
- 通过实例.__proto__可以获取(隐式原型)
- 它们指向了同一个对象构造函数.prototype === 实例.proto
注意:__proto__是浏览器的一个隐藏(私有)属性,IE浏览器不支持,不要通过它来修改原型里的内容,如果要修改原型中的内容,使用构造函数.prototype去修改
代码演示:
<body>
<script>
// __proto__ 属性
// 1. 任何对象有__proto__属性
// 2. 对象的__proto__属性值指向了构造函数的prototype(即原型对象)
function Person(){
}
var p = new Person();
console.log(p);
console.log(p.__proto__);
// 原型对象
console.log( Person.prototype );
console.log( Person.prototype === p.__proto__);
// 要想访问原型对象,途径有2种
// 1. 构造函数通过 prototype 属性来访问
// 2. 实例对象通过 __proto__ 属性来访问
// 注意点:
// __proto__ 该属性不是个标准属性(存有兼容问题),IE678不支持该属性
// 不要在项目中使用__proto__来操作原型对象
// 不推荐:
// p.__proto__.color = "lime";
// console.log(p.__proto__);
// 推荐
Person.prototype.color = "red";
</script>
</body>
5. constructor属性
默认情况下,原型对象中只包含了一个属性:constructor,constructor属性指向了当前的构造函数。
代码演示:
<body>
<script>
// constructor属性
// 1. 是原型对象自带的
// 2. 原型对象的constructor属性的值指向了当前的构造函数
/*function Person(){}
console.log( Person.prototype );
console.log( Person.prototype.constructor );*/
// 练习:
function Person(){}
var p = new Person();
// p.constructor ==> p是去找Person.prototype原型对象要constructor属性
console.log( p.constructor === Person ); // true
console.log( p.constructor === Person.prototype.constructor ); // true
// 构造函数(孩子妈) 原型对象(孩子爸) 实例对象(孩子)
// 构造函数 和 原型对象是配偶关系
// 1. 构造函数 通过 prototype属性 找到 原型对象
// 2. 原型对象 通过 constructor 找到 构造函数
// 构造函数 和 实例对象 是 母子关系
// 1. 构造函数 通过 new 来创建出来实例对象
// 2. 实例对象 不能直接去访问到 构造函数
// 原型对象 和 实例对象的 父子关系
// 1. 实例对象 通过 __proto__属性 找到原型对象(可以使用里面的任意成员)
// 2. 实例对象可以通过原型对象的constructor属性间接的访问构造函数。
</script>
</body>
6. 原型链
1. 原型链概念
任何一个对象,都有原型对象,原型对象本身又是一个对象,所以原型对象也有自己的原型对象,这样一环扣一环就形成了一个链式结构,我们把这个链式结构称为:原型链。
代码演示:
<body>
<script>
/*function Person(){}
var p = new Person();
// 问题: p实例对象使用的toString方法哪来的?
console.log(p.toString());*/
// 原型链:
// 任何对象有 __proto__ 属性, 指向了原型对象,原型对象也是对象,也有__proto__属性,指向了原型对象的原型对象,这样一环套一环形成的链式结构叫做原型链。
// 大白话: 孩子有自己的爸爸,爸爸也有的自己的爸爸 ==> 族谱
function Person(){}
var p = new Person();
// p实例对象的原型链:
// p ==> Person.prototype ==> Object.prototype ==> null;
// console.log( Person.prototype.__proto__ ); // 找爸爸
// console.log( Person.prototype.__proto__.constructor ); // 找妈妈
// console.log( Person.prototype.__proto__ === Object.prototype ); // true
console.log( Object.prototype.__proto__ );
</script>
</body>
内置对象的原型链
代码演示:
<body>
<script>
// 内置对象的原型链
// Array
var arr = new Array(); // arr是数组,实例对象
// 实例对象arr的原型链:
// arr ==> Array.prototype ==> Object.prototype ==> null;
console.log( Array.prototype.__proto__ );
// Date
var d = new Date();
// d实例对象的原型链:
// d ==> Date.prototype ==> Object.prototype ==> null;
// Math
// var m = new Math(); // error Math不是个构造函数,不能new
// Math本身就是个对象
// Math对象的原型链:
// Math ==> Object.prototype ==> null;
// console.log( Math.__proto__ );
// 原型链的特征:
// 任何对象的原型链上都有 Object.prototype
</script>
</body>
2. 属性查找原则
- 在对象自身上来查找是否存在该属性,如果有,就返回对应的值
- 如果没有,就去对象的原型对象中进行查找,如果有,就返回对应的值
- 如果没有,就沿着对象的原型链继续往上找,直到Object.prototype,如果有,就返回对应的值
- 如果在Object.prototype都没有找到,返回undefined
简单记忆:沿着对象的原型链往上找
代码演示:
<body>
<script>
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.name = "lw";
Object.prototype.gender = "male";
var p = new Person("xm", 20);
console.log(p);
// p的原型链来找
// p ==> Person.prototype ==> Object.prototype ==> null;
console.log(p.name); // xm
console.log(p.age); // 20
console.log(p.gender); // male
console.log(p.sex); // undefined
</script>
</body>
练习题
<body>
<script>
// 简单记忆:沿着对象的原型链往上找
/*function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.name = "lw";
Object.prototype.gender = "male";
var p = new Person();
console.log(p);
// p的原型链来找
// p ==> Person.prototype ==> Object.prototype ==> null;
// 注意点:只需要管对象自身上是否有该属性,而不用管值是啥
console.log(p.name); // undefined
console.log(p.age); // undefined
console.log(p.gender); // male
console.log(p.sex); // undefined*/
// 2.
/*function Person(name) {
this.name = name
}
Person.prototype.name = 'ls'
Person.prototype.money = 100
var p = new Person('zs')
console.log(p.name); // zs
console.log(p.money); // 100*/
// 3.
/*function Person(name) {
this.name = name
}
Person.prototype.name = 'ls'
Person.prototype.money = 100
var p = new Person()
console.log(p.name)
console.log(p.money)*/
// 4.
function Person(name) {
// 有形参name,无实参,name为undefined
// if不成立,没有给this添加name属性
if (name) {
this.name = name
}
}
Person.prototype.name = 'ls'
Person.prototype.money = 100
var p = new Person()
console.log(p.name); // ls
console.log(p.money); // 100
</script>
</body>
3. 属性设置原则
- 如果对象有该属性,设置就是在覆盖原来的值,只会影响对象自身,没有影响到原型上的属性
- 如果对象没有该属性,设置就是在给对象添加该属性,只会影响对象自身,没有影响到原型上的属性
简单记忆: 有就覆盖,没有就添加
代码演示:
<body>
<script>
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.name = "lw";
Object.prototype.gender = "male";
var p = new Person("xm", 20);
// p.name = "ls";
p.gender = "不详";
console.log(p.gender); // 不详
console.log( Person.prototype.gender ); // male ==> 去Object原型上查找gender属性
console.log(Object.prototype.gender); // male
</script>
</body>
练习题
<body>
<script>
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 3;
B.prototype.a = 2;
C.prototype.a = 1;
// new A() ==> 得到A的实例对象,实例自身没有a属性,去找A.prototype原型对象上去查找
console.log(new A().a); // 3
// B的实例对象自身有a属性,但值是undefined
console.log(new B().a); // undefined
// C的实例对象自身有a属性,值为2
console.log(new C(2).a); // 2
</script>
</body>