JS高级
javaScript
它是一个编程语言
特点
1.解释执行—— 解释一行执行一行 慢
java C# 编译执行 一次性把代码编译成可执行的代码,然后再一行一行执行 快
2.灵活 动态特性 可以随意给对象增加属性和方法
3.头等函数 函数在JavaScript一等公民
4.执行环境 宿主环境(浏览器)
组成
ECMAScript - 语法规范
变量、数据类型、类型转换、操作符
流程控制语句:判断、循环语句
数组、函数、作用域、预解析
对象、属性、方法、简单类型和复杂类型的区别
内置对象:Math、Date、Array,基本包装类型String、Number、Boolean
Web APIs
BOM onload页面加载事件,window顶级对象
定时器
location、history
DOM 获取页面元素,注册事件
属性操作,样式操作
节点属性,节点层级
动态创建元素
事件:注册事件的方式、事件的三个阶段、事件对象
javaScript面向对象编程
什么是面向对象
面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。
特性: 封装性 继承性 【多态性】抽象
!在javaScript中创建对象的模板是构造函数,而在其他语言中创建对象的模板是类
面向对象编程
—— Object Oriented Programming,简称OOP,是一种编程开发思想
它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟
在面向对象的程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息,处理数据、发出信息等任务
因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发
面向对象与面向编程
面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
面向对象就是找一个对象,指挥得结果
面向对象将执行者转变为指挥者
面向对象不是面向过程的替代,而是面向过程的封装
创建对象
1.new Object()
var hero = new Object(); //空对象
hero.blood = '100';
hero.name = '刘备';
hero.weapon = '剑';
hero.attack = function () {
console.log(this.weapon + '攻击敌人');
}
2.对象字面量
var hero = {}; //空对象
var hero = {
blood: 100;
name: '刘备';
weapon: '剑';
attack: function () {
console.log(this.weapon + '攻击敌人');
}
}
hero.attack();
3.工厂函数 创建多个对象
function createHero(name, blood, weapon) {
var o = new Object();
o.name = name;
o.blood = blood;
o.weapon = weapon;
o.attack = function () {
console.log(this.weapon + '攻击敌人');
}
return o;
}
var hero = createHero('刘备', 100, 剑);(不方便进行判断类型)
构造函数
在内存中创建一个对象 设置构造函数的this,让this指向刚刚创建好的对象
执行构造函数中的代码 返回对象
! 无法使用typeOf获取对象的具体类型
function Hero(name, blood, weapon) {
//实例成员 / 对象成员 -- 跟对象相关的成员,将来使用对象的方式来调用
this.name = name;
this.blood = blood;
this.weapon = weapon;
this.attack = function () {
console.log(this.weapon + '攻击敌人');
}
}
//静态成员 -- 直接给构造函数添加的成员
Hero.version = '1.0';
//静态成员不能使用对象的方式来调用
console.log(hero.version);
//静态成员使用构造函数来调用
console.log(Hero.version);
var hero = new Hero('刘备', 100, '剑');
// 获取对象的具体类型—— console.log(hero.constructor); 不建议
//constructor 构造器 / 构造函数 instanceof 判断某个对象是否是某个构造函数的实例 / 对象
//console.log(hero instanceof Hero);
原型
每一个构造函数都有一个属性 原型 / 原型对象
数组或者String中的 prototype 是不可以修改的
function Student(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Student.prototype.sayHi = function () {
console.log('大家好,我是' + this.name);
}
//第二种设置对象方法
Student.prototype = {
constructor:Student,
sayHi:function () {
console.log('大家好,我是' + this.name);
}
}
//当调用对象的属性或者方法的时候,先去找对象本身的属性/方法,如果对象没有改属性或者方法,此时去调用原型中的属性 / 方法
//__proto__属性是非标准的属性(对象.__proto__ === Student.prototype);
//constructor 记录了创建该对象的构造函数
原型,构造函数和对象的关系
自调用函数
(function () {
connsole.log('1');
})()
继承
面向对象三大特征:封装、继承、多态(抽象)
对象的继承
//继承演示(对象的继承)
function extend(child, parent) {
for(var key in parent) {
if(child[key])continue; //如果child中有该成员,不替换成parent对象中的成员
child[key] = parent[key];
}
}
原型继承
无法设置构造函数中的参数
call()
改变函数中的位置,直接调用函数
function fn(x, y) {
console.log(this);
console.log(x + y);
}
var o = {
name: 'zs'
};
fn.call(o, 2, 3); //第一个参数该表this指向,第二、三参数为x,y
call的应用
//伪数组
var obj = {
0: 100,
1: 10,
2: 11,
3: 20,
length: 4
};
Array.prototype.push.call(obj, 30); //往伪数组中添加一个属性
Bind()
ES5新增的方法(不调用函数)
新建一个方法,bind中的第一个参数可以改变函数中的this的指向
var a = 123;
function fn() {
console.log(this.a);
}
fn(); //此时输出a为123
var o = {a: 'abc'};
var fn1 = fn.bind(o);
fn1(); //相当于 o.fn() 输出a为abc
bind的应用
var obj = {
name: 'zs',
fun: function() {
setInterval(function() {
console.log(this.name);
}.bind(obj), 1000);
}
}
obj.fun() //此时计时器里的this由window变为obj,输出zs
btn.onclick = function () {
//事件处理函数中的this,是触发该事件的对象
}.bind(obj)
apply()
只有两个参数
第一个参数——设置函数内部this的指向,第二个参数——是数组(数组第一个为函数第一个参数以此类推)
apply的应用
var arr = [5, 10, 1, 3, 6];
console.log(Math.max.apply(Math, arr));
console.log.apply(console, arr); //将数组展开传递给前面的方法
借用构造函数
只能够借用父类型的属性,无法借用父类型的方法
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
var s1 = new Student('zs', 18, '男', 100);
console.dir(s1);
组合继承
借用构造函数 + 原型继承
函数进阶
函数声明
function fn() {
console.log('test');
}
fn();
函数表达式
var fn = function () {
console.log('test');
}
//根据条件声明函数
//现代浏览器 不会提升if语句中的函数声明
if(true) {
function fn() {
console.log('fn - true');
}
} else {
function fn() {
console.log('fn - false');
}
}
fn()
区别:
1.函数声明会进行函数提升(预解析),函数表达式不会
2.在if语句中,在现代浏览器中函数声明不会进行函数提升,但在老版本IE浏览器中会进行函数提升
函数表达式则在现代和老版本的浏览器中均不会进行函数提升
new function()
函数也是对象
var fn = new Function('var name = "张三";console.log(a + b)');
fn();
函数中的其他成员
function fn() {
//函数内部有一个私有的变量 arguments
//当函数内部的参数个数不固定的时候
//在函数内部,可以通过arguments获取到实际传过来的参数
console.log(arguments)
//伪数组 获取当时函数的实参
console.log(fn.arguments)
//函数的调用者,在全局范围调用的时候 此时的caller是null
console.log(fn.caller)
//函数的名称 字符串类型
console.log(fn.name)
//函数的形参个数
console.log(fn.length);
}
总结
函数内部的this,是由函数调用的时候来确定其指向的
闭包
Closures(闭包)是使用被作用域封闭的变量,函数,闭包等执行的一个函数的作用域。通常我们用和其相应的函数来指代这些作用域(可以访问独立数据的函数)
闭包是一个函数和函数所声明的词法环境的结合(在一个作用域中可以访问另一个作用域的变量)
闭包特点:延展了函数作用域的范围
闭包的思考
//思考1
var name = 'The Window';
var object = {
name: "My Object",
getNameFunc: function() {
return function () {
return this.name;
};
}
};
console.log(object.getNameFunc()()); //输出The Window
//没有发生闭包,因为console.log()语句在全局作用域中,定义的name变量同样也在全局作用域中,没有做到在一个函数作用域中访问另一个函数作用域的情况。
//相当于 var fn = object.getNameFunc();
// fn();
//思考2
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name;
};
}
};
console.log(object.getNameFunc()()); //输出My Object
//发生了闭包,此时定义that = this ,那么this指向的就是object。在return that.name的时候访问了外部函数作用域中的that值和object中的name属性。
递归
函数自己调用自己
在递归的过程中会出错(Maximum call stack size exceeded)—— 内存溢出,超过了最大的堆栈大小
所以,一般递归都要写一个结束的条件
function fn() {
var n = 10;
console.log(n);
fn();
}
//用递归实现1 + 2 + 3 + 4 + ..... + n
function getSum(n) {
//递归结束条件
if(n === 1) {
return 1;
}
return n + getSum(n - 1);
}
拷贝
浅拷贝
只是复制了对象的一层
function copy(o1, o2) {
for (var key in o1) {
o2[key] = o1[key];
}
}
深拷贝
多层复制
//深拷贝 把o1的成员拷贝给o2
function deepCopy(o1, o2) {
for(var key in o1) {
//获取key属性对应的值
var item = o1[key];
//如果item是对象
if (item instanceof Object) {
o2[key] = {};
deepCopy(item, o2[key]);
} else if (item instanceof Array) {
//如果item是数组
o2[key] = [];
deepCopy(item, o2[key]);
} else {
//如果是简单类型
o2[key] = o1[key];
}
}
}
遍历DOM树
function loadTree(parent, callback) {
for(var i = 0; i < parent.children.length; i++) {
var child = parent.children[i];
if (callback) {
//处理找到的子元素
callback(child);
}
console.log(child);
loadTree(child);
}
}
//利用找到的子元素注册点击事件
var ul = document.getElementById('list');
loadTree(ul, function (element) {
element.onclick = function () {
console.log(this.innerText);
}
});
正则表达式
简介
正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早期研究,现在在编程语言中有广泛的应用。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
作用
1.给定的字符串是否符合正则表达式的过滤逻辑(匹配)
2.可以通过正则表达式,从字符串中获取我们想要的特定部分(提取)
3.强大的字符串替换能力(替换)
特点
1.灵活性、逻辑性和功能性非常的强
2.可以迅速地用极简单的方式达到字符串的复杂控制
3.对于刚接触的人来说,比较晦涩难懂
组成
普通字符abc 123
特殊字符(元字符): 正则表达式有特殊意义的字符\d \w
常用元字符串
元字符 | 说明 |
---|---|
\d | 匹配数字 |
\D | 匹配任意非数字的字符串 |
\w | 匹配字母或数字或下划线 |
\W | 匹配任意不是字母,数字,下划线 |
\s | 匹配任意的空白符 |
\S | 匹配任意不是空白符的字符 |
. | 匹配除换行符以外的任意单个字符 |
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
限定符
限定符 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
其他
1.[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
2.[^] 匹配除中括号以内的内容
3.\ 转义符
4.| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
5.() 从两个直接量中选一个,分组
6.eg: gr(a|e)y匹配gray和grey
7.[\u4e00-\u9fa5] 匹配汉字
正则对象
//内置对象
//第一个参数 模式pattern 字符串类型
//第二个参数flag i忽略大小写 g全局匹配 字符串
var regularExpression = new RegExp('ab[a-z]', 'i');
var str = 'abd';
console.log(regularExpression.test(str));
//new Object() {}
var regularExpression = /ab[a-z]/i;
var str = 'aBd';
console.log(regularExpression.test(str));
跟正则表达式相关的方法
1.RegExp对象
test() 匹配 exec() 提取 提取一个内容
2.String对象
match() 提取 可以提取多个内容 replace() 替换 只能替换第一个查找到的内容
split() 切割 search()
exec()
var str = '张三: 2500, 李四: 3000, 王五: 50000';
var reg = /\d+/g;
var content = reg.exec(str);
console.log(content);
do {
var content = reg.exec(str);
if (content) {
console.log(content[0]);
}
} while(content !== null); //或者直接写成 while(content)
match()
var str = '张三: 2500, 李四: 3000, 王五: 50000';
var reg = /\d+/g;
//提取多个内容
console.log(str.match(reg));
test()
var dataStr = '2015-1-5';
var reg = /(\d{4})-(\d{1,2})-(\d{1,2})/;
reg.test(dataStr);
console.log(RegExp.$1); //$1 - $9分别代表第几个()
replace()
var str = " 123AD asadf asadfasf adf ";
console.log(str.replace(/\s/g, 'x'));
//第二种方法,使用切割
console.log(str.split(' ').join(''));