JavaScript 高级
黑马程序员pink老师的课堂笔记。
JavaScript 高级 知识点速查。
基础可以看主页的另一篇笔记。
目录
面向对象
什么是面向对象
这篇文章写的很好,可以参考一下。
它的特性是:封装性、继承性、多态性。
这里的知识和java
差不多,就不再记录了。
class class_name{
// 构造函数
constructor(para1){
this.para1 = para1;
}
// 类内函数不用写function
fun1(){
//code
}// 多个函数不需要逗号分隔
}
// new创建对象
// ES6 中没有变量提升,需要先定义类才能实例化对象
let 对象 = new class_name("a");
原型
是一个对象,也称prototype 为原型对象。
它的作用是共享方法。可以吧不变的方法直接定义在prototype对象上,这样所有对象的事例就可以共享这些方法。
在对象上,系统自动添加了一个对象原型__proto__
指向我们构造函数的原型对象
方法调用的规则:首先扫描对象事例,如果没有找到相关方法,系统就顺着对象原型搜索他构造函数 的原型对象。
原型对象中this的指向
我们知道在构造函数中的this
指向的是该构造函数的对象实例。
// 构造函数
var that;
function Star(name){
this.name = name;
that = this;
}
// 实例化一个函数
var lhr = new Star('刘昊然');
that === lhr; //true
这里在原型对象函数中也是一样的。
// 原型对象
Star.prototype.sing = function(){
that = this;
}
lhr.sing();
that === lhr; //true
JavaScript 的成员查找机制
- 首先查找对象自身有无该属性
- 没有,查它的原型,即
__proto__
指向的prototype
原型对象 - 还没有,查原型对象的原型,
Object
- 找到
Object
还没有返回null
应用
可以拓展内置对象,为其添加方法:
Array.prototype.ha = function(){
console.log("haha");
}
var array = [1,2];
array.ha();
//输出haha
继承
Call 函数
Call 函数有两个作用:
- 调用函数;
- 修改函数的this指针
function fun(x, y){
console.log(this);
}
// case 1:调用函数
fun.call();
// 输出:指向window
// case 2:修改另一个对象
var obj = {
name: 'a'
}
fun.call(obj, 1, 2);
// 输出:指向obj
使用第二种方法时,第一个参数为修改this指向的对象,后面的可选参数为函数的其他形参。
借用父构造函数继承属性和方法
继承属性
在子构造函数中调用父构造函数,但要注意修改父构造函数的this指针,它指向父构造函数的对象实例。
继承方法
看这个图片
也就是把Son原型对象指向一个Father实例对象,这样可以:
- 继承父类对象,因为这个Father实例对象的原型是Father的原型对象,有Father的方法
money
- 对子类的原型对象添加新的方法时不会影响父类Father
但需要注意将Son的原型对象指向Father实例对象后,需要把原型对象的constructor
指回原来的构造函数Son。
function Father(name){
// this 指向父构造函数的对象实例
this.name = name;
}
Father.prototype.ha = function{
console.log('haha');
}
function Son(name){
// this 指向子构造函数的对象实例
// this.name = name; 未继承
Father.call(this, name);// 继承了属性
}
Son.prototype = Father.prototype;// 子类可以使用父类的构造方法,但是对子类添加方法,父类也会同样拥有(因为两者相同),不符合预期。
Son.prototype = new Father();
// 修改了原型对象,需要把`constructor`指回原来的构造函数Son。
Son.prototype.constructor = Son;
函数进阶
函数定义方式
所有函数都是Function
的实例对象。
常用的函数定义方式:
// 命名函数
function name(para) {};
// 函数表达式 匿名函数
var fun = function(para) {};
// new Function()
// 参数和函数体都需要字符串
var fn = new Function('para1', 'para2'..., 'body');
高阶函数是对其他函数进行操作的函数,它接收函数作为参数,或将函数作为返回值输出。
function fn(a, b, callback){
console.log(a + b);
callback && callback();
}
fn(1, 2, function(){
console.log('我是参数函数');
})
// 输出 3 我是参数函数
函数调用方式和this指针
// 1. 普通函数
fun();// 直接调用
fun.call();// call方法调用
// this指向window
// 2. 对象方法
duixiang.fun();
// 实例化对象后调用
// this指向对象duixiang
// 3. 构造函数
let star = new Star();// new 调用
// 指向star实例对象
// 注意原型对象里面的this指向的也是star这个实例对象
// 4. 绑定事件函数
btn.onclick = function(){};
// this 指向函数的调用者 btn
// 5. 定时器函数
setInterval(function(){}, time);//每隔time毫秒自动调用
// this指向 window
// 6. 立即函数
(function(){})();
// this指向window
修改this指针
call()
方法,见上面apply()
,和call差不多,但是导入参数需要用数组的形式。bind()
,最大的区别是不会调用原来的函数,返回的是原函数改变this之后的新函数。
function fun(x, y){
console.log(this);
}
// case 1:调用函数
fun.call();
// 输出:指向window
// case 2:修改另一个对象
var obj = {
name: 'a'
}
fun.call(obj, 1, 2);
// 输出:指向obj
fun.apply(obj, [1, 2]);
// 输出:指向obj
let fn = fun.bind(obj, 1, 2);
fn();
// 输出:指向obj
闭包closure
变量作用域
全局作用域
直接在script标签中编写的代码都运行在全局作用域中
全局作用域在打开页面时创建,在页面关闭时销毁。
全局作用域中只有一个全局对象window,window对象由浏览器提供,
可以在页面中直接使用,它代表的是整个的浏览器的窗口。
在全局作用域中创建的变量都会作为window对象的属性保存
在全局作用域中创建的函数都会作为window对象的方法保存
在全局作用域中创建的变量和函数可以在页面的任意位置访问。
在函数作用域中也可以访问到全局作用域的变量。
尽量不要在全局中创建变量
函数作用域
函数作用域是函数执行时创建的作用域,每次调用函数都会创建一个新的函数作用域。使用let
定义局部作用域。
函数作用域在函数执行时创建,在函数执行结束时销毁。
在函数作用域中创建的变量,不能在全局中访问。
当在函数作用域中使用一个变量时,它会先在自身作用域中寻找,如果找到了则直接使用,如果没有找到则到上一级作用域中寻找,如果找到了则使用,找不到则继续向上找,一直找不到报错RefrenceError
变量提升
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部。
'use strict';
function foo() {
var x = 'Hello, ' + y;// 不会报错,提升了y的声明
console.log(x);//Hello, undefined
var y = 'Bob';
}
foo();
由于这一怪异的“特性”,我们在函数内部定义变量时,需要**“在函数内部首先申明所有变量”**。
概念
闭包是有权访问另一个函数作用域中变量的函数。
简单来说,一个作用域可以访问另外一个函数内部的局部变量。
主要作用:延伸了变量的作用范围。
浅拷贝和深拷贝
浅拷贝是把更深层次的地址给拷贝了。修改拷贝对象会影响原来的被拷贝对象。
深拷贝:拷贝多层,每一级别的数据都会拷贝,使用新的地址。修改拷贝对象不会影响原来的被拷贝对象。
手写深拷贝
function deepCopy(newobj, oldobj){
// 遍历所有数据
for(var k in oldobj){
// 获取属性值
var item = oldobj[k];
// 数组类型
if(item instanceof Array){
newobj[k] = [];
deepCopy(newobj[k], item);
}else if(item instanceof Object){
// 对象类型
newobj[k] = {};
deepCopy(newobj[k], item);
}else{
// 普通数据类型
newobj[k] = item;
}
}
}
正则表达式
用于匹配字符串中的字符组合
中括号[ ]
:字符集合,匹配括号中的任意字符
大括号{ }
:量词符,表示重复次数
小括号( )
:优先级
// 开始符号和结尾符号
/^ $/
// 字符组合,只能输一个
/^[a]$/ // 只准输a
/^[a-z]$/ // 小写字母
/^[a-zA-Z]$/ // 小大写字母
// 方括号^是取反符号
/^[^a-z]$/ // 不能写小写字母
// 量词符{}
/^abc{3}$/ // 只准输abccc
/^abc{1-3}$/ // ab + 一到三遍的c
/^[abc]{1-3}$/ // 一到三个的a或b或c
预定义类
\d 匹配数字 [0-9]
\D 匹配除数字 [^0-9]
\w 匹配任意字母、数字、下划线
\W 相反
\s 匹配空格(包括换行符、制表符、空格符等)
替换
replace
函数可以用正则表达式进行替换
let input = '我爱你';
console.log(input.replace(/爱{1}/,'*'));
// 我*你
let input = '我爱你爱';
console.log(input.replace(/爱{1}/,'*'));
// 我*你爱
可以看到,这里的replace只对符合条件的第一个文本进行替换,想对全局文本替换,需要使用正则表达式参数。
正则表达式参数
/表达式/[switch]
,其中g
为全局匹配,i
为忽略大小写。
ES6
速查可以看这篇文章。
let
声明变量的关键字,只在所处于的块级有效
const
使用这个关键字声明常量。它具有块级作用域,且声明后值不可更改。
实际上是不能修改数据值本身,也就是值的地址。对于复杂数据结构(如对象),可以修改对象的属性的值(值内部的数据),但是不能修改对象这个值(值本身)。
解构复制
数组结构允许我们按照一一对应的关系从数组中提取值并将值依次赋值给变量。
let ary = [1, 2];
let [a, b] = ary;
// a = 1, b = 2
// 不匹配的为undefined
let[a, b, c] = ary;
// a = 1, b = 2, c = undefined
对象结构允许我们按照使用属性名字作为变量名称来获得对象的属性。
箭头函数
是用来简化函数定义语法的。
() => {
// 函数体
}
// 如果函数体中只有依据代码,且代码的执行结果就是返回值,可以省略大括号
const plus =(a, b) => a + b;
plus(1, 2); // 3
// 如果形参只有一个,形参外侧的小括号也是可以省略的
const db = n => 2 * n;
箭头函数不绑定this 关键字,它的this指向箭头函数定义位置的this。
剩余参数
将一个不定数量的参数用一个数组来表示:...args
例子:
const sum = (...args) =>{
let total = 0;
args.forEach(item => total += item);
return total;
}
sum(1, 2);// 3
sum(10,20,30);// 60
可以配合解构使用:
let ary = [1, 2, 3];
let [p1, ...p2] = ary;
// p1 = 1
// p2 = [2, 3]
扩展运算符
将数组或者对象转换为用逗号分隔的参数序列,它也是用三个点:
let ary = [1, 2, 3];
...ary // 1,2,3
可以用于合并数组:
let ary1 = [1, 2, 3];
let ary2 = [4, 5, 6];
let ary3 = [...ary1, ...ary2];
// 1, 2, 3, 4, 5, 6
String扩展
模版字符串
使用``号(反引号)将模版字符串包裹
let moban = `hello`;
console.log(moban);// hello
特点是模版字符串中可以解析变量,使用${变量名}
就可以引用相关变量:
let name = 'haha';
let moban = `hello, my name is ${name}`;
console.log(moban);// hello,hello, my name is haha
也可以调用函数,输出为函数的返回值。
扩展方法
startsWith()
表示参数字符串是否在原字符串的头部,返回布尔值。
endsWith()
表示参数字符串是否在原字符串的尾部,返回布尔值。