JS小白教程

P01 JavaScript概述

JavaScript是什么?

JavaScript是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言。
直译式:直接解释执行,代码不进行预编译。
脚本:凡是不能独立执行需要依赖其他程序的,通常都叫做脚本。

JS引入方式

标签内引入:直接写在标签内(不推荐)

<button onclick="alert('hello world');">按钮</button>

内部引入:在html文件中直接写在script标签内部

<script type="text/javascript"> //type属性可以省略
    alert('hello world');
</script>

外部引入:在html文件中通过script标签引入外部js文件

<script src="test.js"></script>

常用输出/调试方法

alert( ) 浏览器弹窗,弹出的内容就是()括号中的内容

document.write( ) 向文档写入字符串、html 或 javascript代码

console.log( ) 在。控制台打印相关信息
 
注意:调试代码应当从最终的产品代码中删除掉

常用输出/调试方法

JavaScript大量借鉴了C类语言的语法,但JS的语法更加宽松

JavaScript中的语句以一个分号 ; 结尾

JavaScript多条语句组合的代码块,以 { 开头,以 } 结尾

JavaScript中的一切都区分大小写 (如 变量名、函数名等)

单行注释:
// 注释内容不可以换行
块级/多行注释
/* 注释内容
    可以换行 */

养成良好的代码注释习惯,既方便自己也方便他人!!

标识符指变量、函数、属性或函数的参数的名字。

JavaScript关键字

保留字有可能在将来被用作关键字来使用,不能用作标识符!

break        do          try           typeof
case         else        new           instanceof
catch        in          return        var
continue     for         switch        while
function     this        with          default
if           throw       delete        ......

JavaScript保留字

保留字有可能在将来被用作关键字来使用,不能用作标识符!

abstract     int          short         boolean
export       interface    static        byte
extends      long         super         char
final        native       class         float
throws       const        goto          private
double       import       public        ......

标识符命名规范

标识符指变量、函数、属性或函数的参数的名字

标识符命名规范:

1.第一个字符必须是字母、下划线 _ 或美元符号 $
2.其他字符可以是字母、下划线、美元符号或数字
3.不能含有空格
4.不能以关键字或保留字命名
5.使用驼峰命名法(建议,非必须)

变量的概念及底层原理

变量是指没有固定的值,可以改变的数;是存储信息的容器。

JS的变量是松散类型的,可以用来保存任何类型的数据。

JS中使用关键字 var 来声明变量
关键字  变量名   赋值   数据

 var  userName  =  'xiaocuo';

左值:在等号左侧,是变量名
右值:在等号右侧,是存进变量中的数据

var声明变量的底层原理

从本质上看,变量代表了一段可操作的内存,也可以认为变量是内存的符号化表示。当我们使用关键字var声明一个变量的时候,解析器根据变量的数据类型分配一定大小的内存空间。程序就可以通过变量名来访问对应的内存了。

数据类型

简单数据类型

简单数据类型:string、number、boolean、undefined、null

string 类型,值为字符串
JS中使用双引号或单引号表示字符串

var str1=‘123’;
var str2=“abc”;

number 类型,值为数字
JS中number类型包括整型、浮点型和非数值

var num1 = 123;
var num2 = 123.45;

NaN即非数值,是number类型中的一个特殊值
NaN用于表示本来要返回一个数值的操作数,结果未返回数值的情况(‘a’-1) (‘b’-3)

NaN有两个特点
   1. 任何涉及NAN的操作都会返回NaN
   2. NaN与任何值都不相等,包括它本身

boolean 类型,值为布尔值
boolean类型有两个值 : true 和 false

undefined 类型,值未定义
undefined类型只有一个特殊值为 : undefined

var  und;

声明变量未赋值,这个变量的值就是undefined

null 类型,表示空的对象引用
null类型只有一个特殊值为 : null
如果变量准备在将来用于保存对象,那么该变量最好初始化为null

复杂数据类型

复杂数据类型:object类型

object 类型,值为对象类型
JS中 { } 表示对象,[ ] 表示数组,function 表示函数

var obj1=[1,2,3];
var obj2={‘name’:‘laowang’};
var obj3=function () { };

typeof 操作符

typeof 操作符返回一个用来表示数据类型的字符串。

\u2028使用 typeof 操作符将会返回下列六个字符串之一:
undefined”—值为定义
boolean”—值是布尔值
string”—值是字符串
number”—值是数值
object”—值是对象、数组或null
function”—值是函数

逗号、赋值、算术、关系、逻辑操作符

逗号操作符

使用逗号操作符可以在一条语句中执行多个操作,如:

var a = 1, b = 2, c = 3;

赋值操作符

=    +=    -=    *=    /=    %=

JS中的 ‘=’ 号并非数学计算中的’='号,而是赋值操作符
如:a = 5,应该理解为,把5这个值,值给变量a

var a = 1;
a += 5  -->  a = a + 5;
console.log(a);

算术操作符

加 减 乘 除 余
+  -  *  /  %

console.log(0/3);
console.log(3/0);

% 取余(求模),5%3 即 5对3取余 或 5模3

5%3 == 2              9%3 == 0
2%7 == 2              7%4 == 3
(-2)%7 == -2          (-7)%4 == -3
2%(-7) == 2           (7)%(-4) == 3
(-2)%(-7) == -2       (-7)%(-4) == -3

递增和递减操作符
++ 和 –
++ 表示值递增加1
– 表示值递减减1

var i = 0;

++i 表示先递增,后执行
i++ 表示先执行,后递增

关系操作符
>  <  ==  ===  >=  <=  !=
关系操作符返回布尔值
等于 == 只比较值是否相等
全等 === 先比较类型,再比较值

表达式两侧都是数值 --> 正常比较
表达式两侧都是字符串 --> 正常比较,比较字符的ASCII码值,‘0’ – 48,‘A’ – 65,‘a’ – 97
表达式两侧有一侧是纯数字字符串,该字符串自动转成数值,再进行比较
表达式两侧有一侧是非数字字符串,不能正常比较,所有非正常比较都返回 false

逻辑操作符
逻辑操作符返回布尔值
&&  逻辑与,&&前后均为true才会返回true
||  逻辑或,||前后有一个为true就会返回true
!   逻辑非,!求当前值的相反值

短路操作

var a = 2, b = 3;
(a < b) && (a = 5);
alert(a);  //5

var a = 4, b = 3;
(a < b) && (a = 5);
alert(a);  //4

var a = 2, b = 3;
(a < b) || (a = 5);
alert(a);  //2

var a = 4, b = 3;
(a < b) || (a = 5);
alert(a);  //5

数据类型转换

强制转换(显式转换)

转为字符串

数据.toString( )方法,返回字符串值,不可以转null和underfined

var bool=true;
console.log(true.toString( )); // 'true'
console.log((123).toString( )); // '123'

String( )方法,返回字符串值,所有值都能转
console.log(String(null)); // 'null'
console.log(String(123)); // '123'

转为数值

Number( )方法,将数据转为数值类型

console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number('123')); // 123
console.log(Number('123a')); // NaN

parseInt( ) 与 parseFloat( ) 方法
parseInt( )从左到右检测数据中第一个不为数字的字符,并把之前的所有值返回,如果第一个字符不为数字返回NaN
parseFloat( )与parseInt( )相似,parseInt( )转换整数,parseFloat( )转换浮点数

转为布尔值

Boolean()方法,将数据转为布尔值
undefined、null、0、NaN、''(空字符串)及false,会转成false,其它都会转换成true
console.log(Boolean(2)); // true
console.log(Boolean(0)); // false
console.log(Boolean('abc')); // true
console.log(Boolean('')); // false
console.log(Boolean(null)); // false

自动转换(隐式转换)
x + y,当 + 两侧有一个是字符串类型,另一个是其它类型的时候,会先把其它类型转换成字符串再进行字符串拼接

console.log('123'+true);
console.log(123+'4');

x - y,当 - 两侧都是纯数字字符串,或者一个是纯数字字符串,一个是数字时,会先把字符串转成数字再进行减法运算

console.log('6'-'2');
console.log('6'-2);

还有 * / 与 - 是一样的转换规则

逻辑操作符 && 、|| 、! 也会进行类型转换

!! 两次取反转换,保证了值没有变化,但类型已经被转为布尔值

var str = 'abc';
var bool = !!str;
console.log(bool);

undefined == null相等,ECMA规定的
number == string,会将string转换为number
number == boolean,会将boolean转换为number

非纯数字字符串不能进行 == 比较,全部返回false

进制
进制也就是进位计数制,是人为定义的带进位的计数方法。

十六进制是逢十六进一,十进制是逢十进一,八进制是逢八进一,二进制就是逢二进一 …

在javaScript中进制之间的转换提供了两个非常好用的方法:toString()parseInt()

使用 toString() 方法把十进制转为其他进制:

var  x = 28;// 10进制
console.log(x.toString(2)); //转为2进制  
console.log(x.toString(8));//转为8进制  
console.log(x.toString(16));//转为16进制  

使用 parseInt() 方法把其他进制转为十进制:

var x = "110";//二进制的字符串
console.log(parseInt(x, 2));//把这个字符串当做二进制,转为十进制  

var x = "070";//八进制的字符串
console.log(parseInt(x, 8));//把这个字符串当做八进制,转为十进制  

var x = "0x1c";//十六进制的字符串
console.log(parseInt(x, 16));//把这个字符串当做十六进制,转为十进制 

parseInt() 方法,第一个参数为要转换的字符串,第二个参数指定字符串的进制,默认为十进制

其他进制的相互转换,先使用parseInt变为十进制, 在利用toString变为其他进制。

在javaScript中,八进制以 0 开头,十六进制以 0x 开头,可省略。


P02 逻辑分支

顺序结构

代码一
代码二
代码三

选择结构

true
false
你是帅哥吗
我是帅哥
我是穷屌丝

循环结构

true
false
你是帅哥吗
观察中
帅哥一枚
穷屌丝一个
检测下一个

单分支结构

if 语句

if ( true ) {
    // 执行这里面的代码
}

if ( false ) {
    // 跳过这里面的代码
}

if ( 逻辑点 ) {
    // 执行这里面的代码
} else {// 否则
    // 执行这里面的代码
}

逻辑点 --> 隐式类型转换 --> 布尔值

三元表达式(三元运算符)
(expr1) ? (expr2) : (expr3)
在 expr1 求值为 true 时的值为 expr2,在 expr1 求值为 false 时的值为 expr3。

多个判断语句

if (判断语句1) {
    语句1
} else if (判断语句2) {
    语句2
} else if (判断语句3) {
    语句3
}

if (判断语句1) {
    语句1
} else if (判断语句2) {
    语句2
} else if (判断语句3) {
    语句3
} else {
    语句4
}


多分支结构

switch 语句

switch 语句用于基于不同的条件来执行不同的动作。
switch(n){
    case 1:
        执行代码块 1
        break;
    case 2:
        执行代码块 2
        break;
    default:
        与 case 1 和 case 2 不同时执行的代码
}

工作原理
首先设置表达式 n(通常是一个变量)。
随后表达式的值会与结构中的每个 case 的值做比较。
如果存在匹配,则与该 case 关联的代码块会被执行。
使用 break 来阻止代码自动地向下一个 case 运行。
注:break关键字会导致代码执行流跳出switch语句。


P03 循环结构

循环的概念和意义

循环:指事物周而复始地运动或变化。

在实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。

特征:
1.有规律性的重复操作
2.重复执行的代码极其相似

如:输出10次’hello world’

console.log('hello world 1');
console.log('hello world 2');
console.log('hello world 3');
......
console.log('hello world 10');

这样处理起来非常的费时费力,同时也会有非常多的冗余代码!

假如我要输出 100次 1000次 ‘hello world’ 呢?–> 循环!

for循环

一般形式为:

for (表达式1; 表达式2; 表达式3) {
    循环体;
}

表达式1:为不参与循环的单次表达式,用来给循环控制变量赋初值
表达式2:一般是一个关系表达式,作为循环条件(设置终止值)
表达式3:一般为循环变量增量或减量(步长)
循环体:需要重复执行的代码

for (var i = 0; i < 5; i++) { // 增量循环
    console.log(i);
}

for (var i = 5; i >= 1; i--) {// 减量循环
    console.log(i);
}

image

注意:

for()括号中的表示式皆可以省略,但分号不可省略。

省略了表达式2(循环条件), 若不做其它处理则成为死循环。

死循环:没有终止条件并一直执行的循环即为死循环。

for循环的嵌套,可以简单的理解为行和列的关系。

break 和 continue 关键字

break关键字使用在循环中,他代表终止并跳出循环。

containue关键字使用在循环中,他代表跳过本次循环。

使用 for 和 if 结合,实现跳过某次循环以及符合某个条件退出循环。

while 循环

while 循环只要指定条件为 true,循环就可以一直执行代码。

while (条件){
  需要执行的代码
}

do/while 循环

do/while 循环是 while 循环的变体。

先执行一次 do {} 代码块,再执行 while () 判断。

for、while 和 do-while 的区别

while 循环是先判断,再执行,有可能一次都不执行。

do/while 循环是先执行,再判断,至少执行一次代码块。

for 循环一般用在循环次数可以确定的情景。

while 循环一般用在循环次数未知的情景。


P04 函数的概念及作用

通俗讲:函数就是可重复执行的代码块。

函数的作用

1.通过函数可以封装任意多条语句,以便在任何地方、任何时候调用执行;

2.将代码编写在函数中,就可以避免在非必要情况下调用该代码。

函数的编写及调用

JavaScript中的函数使用function关键字来声明,后跟一组参数以及函数体。

function 函数名(参数) {//函数体
    // do something
}

函数需要调用才能触发:函数名(参数); //调用

系统函数
alert()、parseInt()、prompt()…

自定义函数:
函数声明、函数表达式、匿名函数

**注意:函数声明(任意位置调用)、函数表达式(先定义后调用)、匿名函数(不允许单独定义)

参数的声明及传递:

形参:形式参数,函数定义时,函数的变量

实参:实际参数,函数调用时,要有具体值,实参的值传递给形参且一一对应

参数的意义是让程序更灵活,形参和实参的个数可以不一样,与函数的定义无关, 函数的实参副本 arguments 对象

函数的返回值

所有函数都有返回值!如果函数没有明确的返回值,默认返回 undefined ,使用 return 语句可以自定义返回值。

return 语句会终止当前函数的执行并返回函数的值。

注意:函数内,return 语句之后的所有代码都不会执行!

事件的概念、种类及作用

事件指的是文档或者浏览器窗口中发生的一些特定交互瞬间。

事件是可以被 JavaScript 侦测到的行为。

事件可以提高网页的交互性。

事件:用户的行为

onblur         元素失去焦点
onfocus        元素获得焦点
oninput        用户输入时触发
onchange       用户改变域的内容
onclick        鼠标点击某个对象
ondblclick     鼠标双击某个对象
onkeydown      键盘某个键被按下
onkeyup        键盘某个键被松开
onkeypress     键盘某个键按下并松开
onmousedown    某个鼠标按键被按下
onmousemove    鼠标在某元素上移动
onmouseup      某个鼠标按键被松开
onmouseover    鼠标移到某元素之上
onmouseout     鼠标从某元素移开
onsubmit       form提交时触发
......

非用户行为

onload         某个页面或图像被完成加载
计时器
......

事件与函数的关系

JavaScript是事件驱动的语言!事件通常与函数配合使用,当事件发生时执行对应的函数。事件依赖函数执行,函数可以由事件驱动执行。

事件处理程序 --> 函数

处理元素的样式
  dom.style.样式 = 值;

处理元素的属性
  dom.属性 = 值;

处理元素的内容
  表单元素:dom.value
  非表单元素:dom.innerHTML、dom.innerText

处理交互逻辑
......

this对象

this是js的一个关键字,它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。this的指向在函数运行时才进行绑定。

var box = document.getElementById('box');
box.onclick = function () {
    console.log(this); // this指向box
}

P05 函数的概念及作用二

作用域

在 JavaScript 中, 作用域为可访问变量(包含对象和函数)的集合。通俗讲,作用域就是起作用的范围。
在 JavaScrip 中,有全局作用域和局部作用域。

全局作用域

整个页面起作用,在<script>内都能访问到;

在全局作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用;全局作用域中声明的变量和函数,会作为window对象的属性和方法保存;变量在函数外声明,即为全局变量,拥有全局作用域。

<script>
    var a = 123;//全局变量
    function fn() {
        console.log(a);//123
    }
    fn();
    console.log(a);//123
</script>

在 JavaScrip 中,函数是唯一拥有自身作用域的代码块。

局部作用域

局部作用域内的变量只能在函数内部使用,所以也叫函数作用域;变量在函数内声明,即为局部变量,拥有局部作用域。

<script>
    function fn() {
        var b = 456;//局部变量
        console.log(b);//456
    }
    fn();
    console.log(b);//b is not defined
</script>

可以直接给一个未声明的变量赋值(全局变量),但不能直接使用未声明的变量!
因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。

变量生命周期

全局变量在页面打开时创建,在页面关闭后销毁。

局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。

声明提升机制

在 JavaScrip 中变量声明和函数声明,声明会被提升到当前作用域的顶部。

var a = 1;
function test() {
    alert(a);
    var a = 2;
    alert(a);
}
test();

在同一作用域中:

alert(typeof fn);
var fn = 10;
function fn() {}
alert(typeof fn);

JS解析器
浏览器中有一套专门解析JS代码的程序,将这个程序称为js的解析器。浏览器运行整个页面文档时,遇到<script>标签时JS解析器开始解析JS代码。

JS解析器的工作步骤
1.预解析代码
  主要找一些关键字如varfunction参数等,并存储进仓库里面(内存);
  变量的初始值为 undefined
  函数的初始值就是该函数的代码块;
  当变量和函数重名时:不管顺序谁前谁后,只留下函数的值;
  当函数和函数重名时:会留下后面那个函数。

2.逐行执行代码
  当预解析完成之后,就开始逐行执行代码,仓库中变量的值随时都可能会发生变化

自执行函数

要执行一个函数,我们必须要有方法定位函数、引用函数。

匿名函数如何调用?

自执行函数即声明完了马上进行调用(IIFE立即执行函数)。

(function () {
    console.log(123);
})();

小括号能把我们的表达式组合分块,并且每一块都有一个返回值,这个返回值实际上就是小括号中表达式的返回值。

自执行函数的好处:独立的作用域,不会污染全局环境!

传参:

(function (a,b) {
    console.log(a + b);
})(2,3);

常见形式:

(function () {
    console.log(11);
})();

(function(){
  console.log(22);
}());

!function() {
    console.log(33);
}();

+function() {
    console.log(55);
}();

-function() {
    console.log(66);
}();
......

递归函数

老王有四个子女,老四比老三小2岁,老三比老二小2岁,老二比老大小2岁,老大现在16岁,问老四几岁?

公园里有一堆桃子,猴子每天吃掉一半,挑出一个坏的扔掉,第6天的时候发现还剩1个桃子,问原来有多少个桃子?

阿里巴巴2015年前端面试题

/**
*@desc: fibonacci
*@param: count {Number}
*@return: result {Number} 第count个fibonacci值,计数从0开始
  fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …]
  getNthFibonacci(0) 返回值为1
  getNthFibonacci(4) 返回值为5
*/

image

function f1( ){
    // ...
}
f1( ); // 函数调用
function f2( ){
    f1( ); // 函数调用
}

如果一个函数直接或间接调用函数本身,这个函数就是递归函数。递归函数的本质:函数循环调用.

老王有四个子女,老四比老三小2岁,老三比老二小2岁,老二比老大小2岁,老大现在16岁,问老四几岁?

    function countAge(who) {
        if (who == 1) {
            return 16;
        } else {
            return countAge(who - 1) - 2;
        }
    }
    alert(countAge(4)); // 10

递归函数在运行的时候,每调用一次函数就会在内存中开辟一块空间,内存消耗较大,注意防止栈溢出。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

公园里有一堆桃子,猴子每天吃掉一半,挑出一个坏的扔掉,第6天的时候发现还剩1个桃子,问原来有多少个桃子?

    var x;
    function shuliang(n) {
        if (n == 6) {
            x = 1;
        }else {
            x = (shuliang(n + 1) + 1) * 2;
        }
        return x;
    }
    alert(shuliang(0)); // 190

阿里巴巴2015年前端面试题

/**
*@desc: fibonacci
*@param: count {Number}
*@return: result {Number} 第count个fibonacci值,计数从0开始
  fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …]
  getNthFibonacci(0) 返回值为1
  getNthFibonacci(4) 返回值为5
*/

斐波那契数列:[1, 1, 2, 3, 5, 8, 13, 21, 34…] 数列从第3项开始,每一项都等于前两项之和,斐波那契数列的定义者,是意大利数学家列昂纳多·斐波那契。

function getNthFibonacci(count) {
    var count = parseInt(count);
    if (isNaN(count) || count < 0) {
        return 0;
    }
    function fn(count) {
        if (count <= 1) {
            return 1;
        }
        return fn(count - 1) + fn(count - 2); 
    }
    return fn(count);
}
console.log( getNthFibonacci(0) ); // 1
console.log( getNthFibonacci(4) ); // 5

递归算法一般用于解决三类问题:
    1.数据的定义是按递归定义的;
    2.问题解法按递归算法实现;
    3.数据的结构形式是按递归定义的。

构造函数及对象类型(了解)

构造函数:用于创建特定类型的对象。JS内部构造函数:Object、Number、String、Array、Function、Boolean等等…当任意一个普通函数用于创建一类对象,并通过new操作符来调用时它就可以作为构造函数。

构造函数一般首字母大写。


P06 对象 Object

对象的概念及操作

对象是一组无序的键值对,是带有属性和方法的集合。

通俗讲,对象就是无序的数据集合。

属性是与对象相关的值,方法是能够在对象上执行的动作。

调用:对象.属性   对象.方法()

对象的作用:用于在单个变量中存储多个值。

定义对象:

var obj1 = { };
obj1.name = '小平平';
obj1.age = 18;
obj1.sayHi = function (){
    alert('hi,大家好');
}
console.log( obj1.name );
obj1.sayHi( );
 
var obj2 = new Object();
obj2.name = '小平平';
obj2.age = 18;
obj2.sayHi = function (){
    alert('hi,大家好');
}
console.log( obj2.name );
obj2.sayHi( );

对象字面量:(JSON对象格式)

var obj = { 键:值, 键:值 ...... };

键:一般用双引号引起来(不用引号也可以)
值:可以是任意类型的数据

var obj = {
    name: '小平平',
    age: 18,
    sayHi: function (){
        alert('hi,大家好');
    }
}

对象类型可以动态添加属性和方法!简单类型不可以添加属性和方法!

数组 Array

数组的概念及操作

数组,是有序的元素序列。

通俗讲,数组就是有序的数据集合。

数组属于对象类型。

数组的作用:用于在单个变量中存储多个值。

创建数组:

var arr1 = [ ];

var arr2 = new Array( );

var arr3 = new Array( size );

var arr4 = new Array( el1, el2, el3 ... );

基本操作:

var arr5 = ['a', 'b', 'c'];

访问数组元素:arr5[0]、arr5[1]、arr5[2]

中括号里的数字为数组的索引值,数组的索引总是从0开始!

设置数组元素:arr5[1] = 2;  arr5[4] = 5;

获取数组的长度(数组中元素的个数):arr5.length
数组的操作方法:

push()在数组的后面添加(单个或多个)新元素,返回数组新的长度

pop() 删除数组的最后一个元素,返回被删除的元素

unshift()在数组的前面添加(单个或多个)新元素,返回数组新的长度

shift()删除数组的第一个元素,返回被删除的元素

splice() 可以对数组进行增、删、改,返回一个数组(被删除的元素)

删除n项:arr.splice(起始位置,n)

增加元素:arr.splice(起始位置,0,添加1,添加2…)

修改元素:arr.splice(起始位置,2,替换1,替换2)

sort() 将数组进行排序,返回数组

arr.sort(); //默认按照字符排序

reverse() 将数组进行倒转,返回数组

arr.reverse();

以上方法都会改变原数组!

slice() 从数组中拷贝一部分,返回新数组
arr.slice(1,4) 包含索引为1的元素,不包含索引为4的元素(拷贝索引为 1 2 3 的元素)

concat() 用于合并数组,返回新数组
arr.concat(arr1,arr2......)

toString() 将数组转成字符串,返回字符串

join() 将数组转成字符串,参数为分隔符,返回字符串

以上方法都不会改变原数组!

数组的遍历:

for 循环

for/in 循环
没有循环条件,自动取出下标,下标为字符串
一般用于遍历对象,不推荐数组使用

对象数组/二维数组

var arr1 = [
    {name: '小明', age: 18},
    {name: '小红', age: 18},
    {name: '小东', age: 19}
];

var arr2 = [
    [1,2,3],
    [4,5,6],
    [7,8,9]
];

基本类型和引用类型

基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象。

基本类型:numberstringbooleannullundefined

基本数据类型是保存在栈内存中的简单数据段,是按值访问的,可以直接操作保存在变量中的实际值。

var a = 10;
var b = a;
b = 20;
console.log(a);

变量b只是保存了变量a复制的一个副本,所以变量b的改变,对变量a没有影响。

引用类型:对象类型,如 ObjectArrayFunction 等等

引用数据类型是保存在堆内存中的对象。

在JavaScript中不可以直接操作堆内存中的对象,只能操作对象在栈内存中的引用地址。

var obj1 = {name: '老王', age: 28};
var obj2 = obj1;
obj2.name = '老赵';
console.log(obj1.name);
console.log(obj2.name);

var obj2 = obj1; 在栈内存中把堆内存对象的引用地址复制一份给obj2。

意味着 obj1和obj2 指向同一个堆内存对象。

obj2.name = '老赵'; 实际上改变的是堆内存对象。

所以,obj1.name和obj2.name 都是'老赵'。

image
示例:函数的值传递和引用传递

数组的排序

sort() 将数组进行排序,返回数组

arr.sort(); //默认按照字符排序

arr.sort(function (a,b) { //升序,只能对数值排序
    return a-b;
});
arr.sort(function (a,b) { //降序,只能对数值排序
    return b-a;
});

冒泡排序:相邻两个数进行比较,大数下沉,小数上浮

function fnSort(arr){
    var t = 0;
    for( var  i = 0 ;  i < arr.length-1; i ++ ){ // 控制比较轮数
        for( var j = 0; j < arr.length-1-i ; j++ ){ //每一轮比较次数
            if( arr[j] > arr[j+1] ){
                t = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = t;
            }
        }
    }  
    return arr;
}

P07 ES5

严格模式
除了正常运行模式,ECMAscript5添加了第二种运行模式:“严格模式”(strict mode)。
顾名思义,这种模式使得Javascript在更严格的条件下运行。

设立"严格模式"的目的,主要有以下几个:
   1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
   2.消除代码运行的一些不安全之处,保证代码运行的安全;
   3.提高编译器效率,增加运行速度;
   4.为未来新版本的Javascript做好铺垫。

进入"严格模式"的标志:"use strict";

"use strict"; 放在脚本文件的第一行,则整个脚本都将以"严格模式"运行。

<script>
   "use strict";
   console.log("这是严格模式。");
</script>

"use strict"放在函数体的第一行,则整个函数以"严格模式"运行。

function strict(){
   "use strict";
   return "这是严格模式。";
}

严格模式对JS的语法和行为有一些限制:
给未声明的变量赋值直接报错,而不是变成全局变量;
禁止this关键字指向全局对象;
对象不能有重名的属性(IE);
函数不能有重名的参数;

ES5支持情况:ie9+, chrome 19+, safari 5+, firefox 4+, opera 12+

ES5新增数组常见方法
indexOf() 方法返回数组中第一个找到的元素位置,如果它不存在则返回-1

forEach() 方法为每个元素执行对应的方法

filter() 方法返回一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素

map() 方法对数组的每个元素进行一定操作后,返回一个新的数组

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值

array.reduce(function(total, item, index, array), initialValue)

total:初始值, 或者计算结束后的返回值
initialValue:初始值

字符串 string
JavaScript的字符串就是用’'或""括起来的字符。

字符串之间使用 + 号进行拼接。

创建字符串:

var str1 = 'abc';
var str2 = new String('abc');

length-属性返回字符串的长度(字符的个数)
str1.length; // 3

可以通过索引访问字符串中的某个字符:str1[0]、str1[1]

字符串常见API
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数

toLowerCase() 方法将整个字符串转成小写字母

toUpperCase() 方法将整个字符串转成大写字母

indexOf() 方法返回字符串中子串第一处出现的索引值,没有匹配返回-1

lastIndexOf() 方法返回字符串中子串最后出现的索引值,没有匹配返回-1

slice() 方法从已有字符串中提取部分字符,返回新的字符串

var str = str1.slice(start,end);

slice()返回的子串包括start处的字符,但不包括end处字符

split() 方法把一个字符串分割成字符串数组,返回新数组

var str = str1.split('分割符',length);

第一个参数指定分割的符号,第二个参数可选,为返回数组的长度

substr() 方法返回一个从指定位置开始的指定长度的子串

var str = str1.substr(start,length);

参数start必须,字符的起始位置,length参数可选,截取字符串的长度

substring() 方法返回字符串中介于两个指定下标之间的子串

var str = str1.substring(start,end);

包含start处的字符,不包含end处的字符

concat() 方法将两个或多个字符串组合起来,返回一个新的字符串

charAt() 方法返回指定索引位置的字符

replace() 方法用于在字符串中用一些字符替换另一些字符,返回替换后的字符串

var newStr = str.replace('abc','替换abc');

charCodeAt() 方法返回指定索引处字符的unicode编码值

String.fromCharCode(num1,num2,num3…) 方法根据指定的Unicode编码值来返回字符串

var str = String.fromCharCode(65,66,67);// 'ABC'

ASCII、Unicode与编码字符集
编码字符集:(简称字符集,如Unicode、ASCII)
编码字符集,用一个编码值来表示一个字符在库表中的位置,这个值称为字符对应于编码字符集的序号。

最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码。

ASCII编码主要针对的是英语,全世界有上百种语言,中国制定了GB2312编码,日本制定了Shift_JIS编码,韩国制定了Euc-kr编码…

各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

Unicode编码把所有语言都统一到一套编码里,这样就不会再有乱码问题了!

Unicode 编码是一个很大的集合,现在的规模可以容纳100多万个符号。
Unicode 编码开头的 128 个和 ASCII 编码一样。
编码字符集Unicode,有UTF-8、UTF-16、UTF-32等多种字符编码。

Unicode编码汉字对照表:http://www.chi2ko.com/tool/CJK.htm


P08 Math·Date 对象 和 计时器

Math对象及常见API

Math 对象用于执行数学任务。

Math是一个内置对象,不需要创建,可以直接使用。

Math.PI --------------返回圆周率3.1415926...
Math.ceil(x) ---------对数值x进行向上取整
Math.floor(x) --------对数值x进行向下取整
Math.round(x) --------对数值x进行四舍五入
Math.abs(x) ----------返回x的绝对值
Math.pow(x,y) --------返回x的y次方
Math.sqrt(x) ---------返回x的平方根
Math.min(a,b,c...) ---返回abc...中的最小值
Math.max(a,b,c...) ---返回abc...中的最大值
Math.random() --------返回介于[0,1)之间的随机数

注意:Math.random() 理论上能得到0,实际使用几乎不可能得到0

计时器

setInterval ( 函数 , 毫秒数 )
表示每经过一定的毫秒后,执行一次相应的函数(重复)

setTimeout ( 函数 , 毫秒数 )
表示经过一定的毫秒后,只执行一次相应的函数(不重复)

清除计时器:
clearInterval( timerId );
clearTimeout( timerId );

Date 对象

Date对象及常见API
Date 对象用于处理日期与时间。

Date 对象自动使用当前系统的日期和时间作为其初始值。

创建Date对象:

var d = new Date();
console.log(d);
VM971:2 Wed Mar 25 2020 21:43:27 GMT+0800 (中国标准时间)

UTC国际标准时间又称世界时,以零经度线上的时间作为国际上统一采用的标准时间。
因为零经度线通过英国格林尼治天文台,所以国际标准时间也称为格林尼治时间GMT。
国际标准时间的起点:1970/01/01 00:00:00
北京时区的时间起点:1970/01/01 08:00:00
所以,北京时间 = 国际标准时间 + 8小时

获取:

getFullYear()------从 Date 对象以四位数字返回年份。
getMonth()---------从 Date 对象返回月份 (0 ~ 11)。
getDate()----------从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getHours()---------返回 Date 对象的小时 (0 ~ 23)。
getMinutes()-------返回 Date 对象的分钟 (0 ~ 59)。
getSeconds()-------返回 Date 对象的秒数 (0 ~ 59)。
getMilliseconds()--返回 Date 对象的毫秒(0 ~ 999)。
getDay()-----------从 Date 对象返回一周中的某一天 (0 ~ 6)。
getTime()----------返回1970年1月1日至今的毫秒数。

返回一个’年月日 时分秒’的本地格式字符串:

d.toLocaleString(); // "2020/3/25 下午9:43:27"

返回一个’年月日’的本地格式字符串:

d.toLocaleDateString(); //"2020/3/25"

设置:

setFullYear()------设置 Date 对象中的年份(四位数字)。
setMonth()---------设置 Date 对象中月份 (0 ~ 11)。
setDate()----------设置 Date 对象中月的某一天 (1 ~ 31)。
setHours()---------设置 Date 对象中的小时 (0 ~ 23)。
setMinutes()-------设置 Date 对象中的分钟 (0 ~ 59)。
setSeconds()-------设置 Date 对象中的秒钟 (0 ~ 59)。
setMilliseconds()--设置 Date 对象中的毫秒 (0 ~ 999)。
setTime()----------设置 Date 对象(向1970/1/1添加毫秒数)。

创建指定时间点的Date对象:

var d1 = new Date(毫秒数); //从时间起点开始叠加的毫秒数
var d2 = new Date('yyyy/MM/dd HH:mm:ss');

返回当前时间与起始时间之间的毫秒数:

Date.now(); // 1585143973516

返回转换后的Date对象与起始时间之间的毫秒数:

Date.parse('2020/03/25 00:00:00'); // 1585065600000

P09 DOM

DOM的概念及作用

Document   Object   Model -- DOM
      文档          对象      模型

DOM是针对HTML和XML文档的一个API(应用程序编程接口)。

DOM描绘了一个层次化的节点树,即HTML文档中的所有内容都是节点(node)。
DOM树中的所有节点均可通过JS进行访问,允许开发人员添加移除修改查询页面的某一部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u3IF6mKW-1587348864375)(http://csdn.raomaiping.xyz/o_200325140322Image.png)]

image

节点类型

整个文档是一个文档节点,每个HTML元素是元素节点,HTML元素内的文本是文本节点(回车也是文本节点)
每个HTML的属性是属性节点,注释是注释节点。

nodeType 属性:返回一个整数,这个数值代表节点的类型
元素节点  返回 1      
属性节点  返回 2      
文本节点  返回 3       
注释节点  返回 8      
文档节点  返回 9

nodeName 属性:返回节点的名称
元素节点的 nodeName 是标签名称   ( 大写 )
属性节点的 nodeName 是属性名称
文本节点的 nodeName 永远是 #text
注释节点的 nodeName 永远是 #comment
文档节点的 nodeName 永远是 #document

nodeValue 属性:返回节点的值
对于元素节点,nodeValue 返回值是 undefined 或 null
对于文本节点,nodeValue 返回文本内容
对于属性节点,nodeValue 返回属性值
对于注释节点,nodeValue 返回注释内容
对于文档节点,nodeValue 返回 null

对于元素节点,用 innerHTML/innerText / value 设置或取值

节点关系

节点之间的关系,通常用家庭中的辈分关系来描述。

祖先 -> 父辈 -> 子女(兄弟姐妹) -> 子孙

parentNode:父节点
children:所有是标签类型的子节点
childNodes:所有子节点
nextSibling:下一个兄弟节点
previousSibling:上一个兄弟节点
firstChild : 第一个子节点
lastChild:最后一个子节点

nextElementSibling
previousElementSibling
firstElementChild
lastElementChild

image

节点方法

createElement(“标签名”) : 创建元素节点
createTextNode(“”) : 创建文本节点
var newNode=document.createElement('div');
var textNode=document.createTextNode('文本内容');

appendChild(node) : 末尾插入一个节点node
insertBefore(node,target) : target之前插入节点node
removeChild(node) : 移除某个子节点
replaceChild(newNode,oldNode) : newNode替换oldNode

document.getElementById('');
document.getElementsByName('');
document.getElementsByTagName('');
document.getElementsByClassName('');
document.querySelector('');
document.querySelectorAll('');

cloneNode(boolean) : 复制一个节点
    true:深复制,复制节点及其整个子节点树
    false : 浅复制,只复制节点本身。
注:不会复制添加到DOM节点中的JS属性,例如事件处理程序等。

getAttribute(“name”)    获取节点上name属性的值
setAttribute(“name”,“value”)    设置节点上name属性的值为value
removeAttribute(“name”)     删除节点上的name属性
getAttributeNode(“type”)    获取节点上type属性节点

获取元素样式

行间样式与非行间样式

行间样式:
<div id="box1" style="width:300px; height:100px;"></div>
console.log(box1.style.width);

非行间样式:
#box2 {width: 200px; height: 50px;}
<div id="box2"></div>
console.log(box2.style.width);

getComputedStyle:获取非行间样式(IE678除外)
如:getComputedStyle(对象,参数).样式
第一个参数是要获取样式的元素对象
第二个参数可以传递任何数据,通常为false或null
 
currentStyle:IE678获取非行间样式的方法
如:obj.currentStyle.样式

offset/client系列属性

offsetLeft:获取对象左侧与定位父级之间的距离(默认是body)
offsetTop:获取对象上侧与定位父级之间的距离(默认是body)
offsetWidth:获取元素自身的宽度(包含边框)
offsetHeight:获取元素自身的高度(包含边框)

clientLeft、clientTop:获取元素内容到边框的距离,效果和边框宽度相同,很少使用
clientWidth:获取元素自身的宽度(不含边框)
clientHeight:获取元素自身的高度(不含边框)

document.documentElement.clientWidth  可视区宽度
document.documentElement.clientHeight  可视区高度

document.body.clientWidth  body的宽度
document.body.clientHeight  body的高度

document.documentElement.offsetWidth  整个文档宽度
document.documentElement.offsetHeight  整个文档高度


P10 BOM

BOM的概念及作用

Browser Object Model -- BOM
  浏览器    对象    模型

BOM提供了独立于内容而与浏览器窗口进行交互的对象,核心对象是window

JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C,BOM缺乏标准

BOM最初是Netscape浏览器标准的一部分

DOM是为了操作文档节点出现的API,document是其的一个对象

BOM是为了操作浏览器对象出现的API,window是其的一个对象

window对象
 
window对象是浏览器中的Global对象

var newWindow = window.open(URL,name,specs);  返回一个新窗口
URL:打开页面的URL,没有指定URL将打开新的空白窗口
name:_blank  新窗口打开,默认
            _self  当前页面打开
      ......
specs:一个逗号分隔的项目列表。支持以下值:
            width=pixels    height=pixels  最小值为100
            left=pixels        top=pixels    ......
示例 window.open('','','width=200,height=200');

window.close() 方法用于关闭浏览器(新)窗口;
某个新窗口可以通过调用 close() 来关闭其自身;
通过 JavaScript 代码打开的新窗口才能够由 JavaScript 代码关闭;
这阻止了恶意的脚本终止用户的浏览器。
 
调整窗口大小 window.resizeTo(width,height);
调整窗口大小 window.resizeBy(width,height);
注:此功能在一些标签型浏览器中无效。
 
window.screenLeft 属性返回窗口相对于屏幕的X坐标
window.screenTop 属性返回窗口相对于屏幕的Y坐标
window.screenX 属性返回窗口相对于屏幕的X坐标
window.screenY 属性返回窗口相对于屏幕的Y坐标
 
window.setInterval ( 函数/名称 , 毫秒数 )
表示每经过一定的毫秒后,执行一次相应的函数(重复)
window.setTimeout ( 函数/名称 , 毫秒数 )
表示经过一定的毫秒后,只执行一次相应的函数(不重复)
清除计时器:clearInterval( );     clearTimeout( );
 
提示框 alert (“ ”);
用户必须先关闭该消息框然后才能继续进行操作
确认框 confirm(“ ”);

confirm(“需要确认的内容”);

选择“确定”返回true        选择“取消”返回false
输入框 prompt(“ ”,“ ”);

prompt(“对话框的提示文本”,"默认的输入文本");

单击取消,则返回 null;单击确认,则返回输入的文本

history对象
 
history对象包含有关用户的访问历史记录

length 返回浏览器历史列表中的 URL 数量
forward() 加载 history 列表中的下一个 URL
back() 加载 history 列表中的上一个 URL
go() 加载 history 列表中的某个具体页面
history.go(-1)    后退一页
history.go(1)    前进一页

location对象
 
location对象包含有关当前页面的URL信息

host 属性设置或返回主机名和当前 URL 的端口号
port 属性设置或返回当前 URL 的端口号
href 属性设置或返回完整的 URL    ……
assign() 方法加载新的文档
reload() 方法重新加载当前文档
replace() 方法用新的文档替换当前文档

navigator对象
 
navigator对象用于提供与用户浏览器相关的信息

appCodeName 属性返回浏览器的代码名
appName 属性返回浏览器的名称
cookieEnabled 属性返回指明浏览器中是否启用cookie的布尔值
platform 属性返回运行浏览器的操作系统平台

appVersion 属性返回浏览器的平台和版本信息
userAgent 属性返回用户浏览器发送服务器的user-agent头部的值

识别浏览器

var str1=window.navigator.userAgent;
var str2=window.navigator.appVersion;
结合indexOf( )和toLowerCase( )方法可识别用户浏览器

screen对象
 
screen对象包含有关客户端显示屏幕的信息

width 属性返回显示器屏幕的宽度
height 属性返回显示器屏幕的高度
availHeight 属性返回显示屏幕的高度 (除 Windows 任务栏之外)
availWidth 属性返回显示屏幕的宽度 (除 Windows 任务栏之外)

P11 event事件对象

event概念及作用
 
事件通常与函数结合使用,函数不会在事件发生前被执行;event事件对象只在事件发生的过程中才有效;event对象中包含了所有与事件相关的信息(私有的、共有的属性和方法)。

在需要获取和事件相关的信息时使用,如:
获取键盘按下的按键码
获取鼠标的位置坐标
获取事件名称
获取事件生成的时间
获取事件的类型
等等…

获取event对象

所有浏览器都支持event对象,只是支持的方式不一样
FireFox、Chrome等浏览器要获取到event对象,需要从函数中传入,参数名随意
而IE在浏览器中event作为window对象的一个属性存在,可以直接使用 event 或 window.event 获取到
例如:

document.onmousedown=function ( e ){
    var ev = e || window.event ; //兼容各个浏览器
    console.log(ev);
};
注意 var ev = e || window.event ; 顺序!

鼠标/键盘相关属性

clientX/clientY属性:返回当事件被触发时,鼠标指针的坐标(到浏览器窗口的坐标)

offsetX/offsetY属性:返回当事件被触发时,鼠标指针的坐标(在事件源元素中的坐标)

pageX/pageY属性:返回当事件被触发时,鼠标指针的坐标(整个页面中的坐标)

button属性:返回触发事件的鼠标按键
which属性:返回触发事件的按键码(针对键盘和鼠标事件)
keCode属性:返回键盘按键的按键码
组合键:ctrlKey、altKey、shiftKey

其他属性/方法

在火狐中获得触发事件的元素
使用:event.target
在IE中获得触发事件的元素
使用:event.srcElement

// 兼容写法
var Target = event.target || event.srcElement;

event.relatedTarget  火狐获取触发事件的目标源,与target相反
event.fromElement  IE获取触发事件的目标源,与srcElement相反
mouseover事件中,它指向鼠标来自哪个元素

var from=event.relatedTarget || event.fromElement;

event.relatedTarget  火狐获取触发事件的目标源,与target相反
event.toElement  IE获取触发事件的目标源,与srcElement相反
mouseout事件中,它指向鼠标去往的那个元素

var to=event.relatedTarget||event.toElement;

阻止浏览器默认行为

event.preventDefault()  //火狐
event.returnValue = false  //IE
event.preventDefault ? event.preventDefault() : (event.returnValue = false);

阻止事件传播

event.stopPropagation()  //火狐
event.cancelBubble = true  //IE
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);

scroll 滚动条

scrollLeft:设置或获取当前左滚的距离,即左卷的距离;
scrollTop:设置或获取当前上滚的距离,即上卷的距离;
scrollHeight:获取对象可滚动的总高度;
scrollWidth:获取对象可滚动的总宽度;

onscroll 事件在元素滚动条在滚动时触发。

P12 事件模型

JavaScript中的两种事件模型:DOM0,DOM2。
 
DOM0级事件模型

DOM0级事件模型是早期的事件模型,所有的浏览器都支持。
注册事件:在事件类型前面加on,如:onclick、onmouseover …
解除事件:dom.onclick = null;

每个DOM对象只能注册一个相同类型的事件,注册多个则会发生覆盖,只执行最后一个事件函数。

DOM2级事件模型

DOM2级事件模型是较新的事件模型,IE8及以下是不支持的。

注册事件:

addEventListener(type,fn,useCapture) 事件监听

参数:type----事件类型,例:click
          fn----事件处理函数
          useCapture----布尔值true或false
         (true表示事件捕获,false表示事件冒泡)

为了兼容浏览器,第三个参数一般设置为false
解除事件:removeEventListener(type, fn, useCapture)

每个DOM对象可以注册多个相同类型的事件,不会发生覆盖,会依次的执行各个事件函数。

因为IE只支持事件冒泡,不支持事件捕获,所以它也不支持addEventListener( )方法

IE提供了另一个函数attachEvent( type , fn )

*参数:type----事件类型,例:onclick
          fn----事件处理函数
          没有第三个参数

解除事件:detachEvent( type , fn )*

事件流

事件流:事件的流向,事件的执行顺序。

当子元素和父元素都定义了相同的事件,比如都定义了onclick事件,点击子元素时,父元素的onclick事件也会被触发。

JS里称这种事件连续发生的机制为事件冒泡或者事件捕获。

IE:事件从里向外发生,事件从最精确对象(target)开始触发,然后到最不精确的对象(document)触发,即事件冒泡

网景:事件从外向里发生,事件从最不精确的对象(document)开始触发,然后到最精确对象(target)触发,即事件捕获

W3C将两者进行中和,在任何W3C的事件模型中,事件先进入捕获阶段,再进入冒泡阶段

image

不管是事件冒泡,还是事件捕获,都有传播的特征!

阻止事件传播

event.stopPropagation()  // W3C
event.cancelBubble = true  // IE
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);

事件委托

什么是事件:通俗讲onclick,onmouseover,onmouseout等就是事件。

什么是委托:就是这个事件本来是加在某些元素上的,而你却加到其他元素上来完成这个事件。

原理:利用事件传播的特征。
具体来说,事件委托就是事件目标自身不处理事件,而是把处理事件委托给其父元素,甚至是document 来完成。

优点

  1. 提高性能和效率

  2. 减少事件注册,节省内存占用

  3. 未来元素无需再次注册事件

鼠标滚轮事件

在非火狐浏览器中
鼠标滚轮事件:onmousewheel
示例:

box.onmousewheel=function (ev){
     var event=ev||window.event;
     alert(event.wheelDelta);
};

在火狐浏览器中
鼠标滚轮事件:DOMMouseScroll
示例:

box.addEventListener('DOMMouseScroll',function (ev){
     alert(ev.detail);
},false)

P13 正则表达式

JavaScript最初诞生的目的是什么?

// 从杂乱字符串中找出数字:
var str='dadf123dsafd33dfds;;;[]123dg;123'
var arr1=[];
for(var i = 0; i < str.length; i++){
     if(str.charAt(i) >= 0 && str.charAt(i) <= 9){
        arr1.push(str.charAt(i));
     }
}
console.log( arr1.join('') );


// 懒人推动科技进步!
var arr2 = str.match(/\d+/g);
console.log( arr2.join('') ); 

正则表达式

正则表达式,又称规则表达式。

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式通常用来检索、替换那些符合某个规则的文本。

定义正则表达式有两种形式:

构造函数方式: new RegExp(pattern, attributes);
例:var reg=new RegExp(“abc","g");
构造函数中的正则可以是常量字符串或一个JS变量

pattern 为一个字符串或匹配规则
attributes 为可选字符串,包含属性g、i 和 m
    g:代表全局匹配 (继续往下匹配)
    i:代表不区分大小写匹配
    m:代表多行匹配,只对^和$模式有用

普通方式: /pattern/attributes(/正则/属性)
例:var reg=/abc/g;

普通方式中的正则必须是一个常量字符串

正则的索引

每个正则表达式都有一个 lastIndex 属性,用于记录上一次匹配结束的位置(索引值)。

获取:reg.lastIndex;    设置:reg.lastIndex=0;
示例:(test:方法检索字符串中指定的值  返回 true 或 false)
var str=‘abcdeabfg’;
var reg=new RegExp(‘ab’,‘g’);
alert(reg.test(str));
alert(reg.lastIndex);
alert(reg.test(str));
alert(reg.lastIndex);

元字符

\d : 匹配任意一个数字,0~9 中的任意一个。
\s : 匹配任意一个空白字符。
\b : 匹配一个单词边界,不匹配任何字符。
\w : 匹配任意一个字符( 字母、 数字、下划线 )。
.    : 匹配任意一个字符,除了换行符( \n )。
\n : 查找换行符。
\u : 通常用来匹配汉字。
表达式 "\d","\s","\b","\w",对应的大写字母表示相反的意义。
如:\D  匹配所有的非数字字符(反选)。

方括号

[abc] : 查找方括号之间的任何字符。
[^abc] : 查找任何不在方括号之间的字符(反选)。
[0-9] : 查找任何从 0 至 9 的数字。
[a-z] : 查找任何从小写 a 到小写 z 的字符。
[A-Z] : 查找任何从大写 A 到大写 Z的字符。

量词

n+ : 匹配任何包含至少一个 n 的字符串。
n? : 匹配任何包含零个或一个 n 的字符串。
n* : 匹配任何包含零个或多个 n 的字符串。
n{X} : 匹配包含 X 个 n 的序列的字符串。
n{X,Y} : 匹配包含 X 到Y 个 n 的序列的字符串。
^n : 匹配任何开头为 n 的字符串。
n$ : 匹配任何结尾为 n 的字符串。

其他

|   : 匹配左边或者右边
\   : 转义符  
特殊标点符号,在前面加 \ 后,就代表该符号本身
^ 要匹配 "^" 字符本身,请使用 \^
( ) 要匹配小括号本身,请使用 \(  和  \)
其他特殊标点符号  [ ]  { }  .   ?    +   *   |
Unicode编码16进制的utf-8汉字编码:
4e00最小中文字符    9fa5最大中文字符
/[\u4e00-\u9fa5]+/  常用汉字编码范围
var reg=/[\u4e00-\u9fa5]+/g; //匹配汉字
var str='123jlkj她637skfjkdd';
console.log(reg.test(str));

正则对象方法

test:检索字符串中指定的值  返回 true 或 false
reg.test(str);
exec: 检索字符串返回查找结果的第一个值
reg.exec(str);
compile: 该方法可以重编辑指定的正则表达式
var num='13520006789';
var reg=/13[4-9]\d{8}/g;
console.log(reg.test(num));
reg.compile('13[0-3][0-9]{8}','g');//修改正则
console.log(reg.test(num));

String对象方法

search:检索与正则表达式相匹配的值
用法:str.search(reg);  (返回索引值,无-1)
match:返回所有正则表达式的匹配(加g)
用法:str.match(reg);  (返回数组)
replace:替换与正则表达式匹配的子串
用法:str.replace(reg,“”);  返回替换之后的内容
split:将字符串匹配的部分做分割(去除)
用法:str.split(reg); (返回数组)

例子:

过滤HTML标签
尖括号里是除了尖括号之外其他字符  /<[^<>]+>/g

匹配邮政编码
开头非0的6位数字  /^[1-9]\d{5}$/

文件格式检测
文件格式结尾为 .格式   
/\.(png|jpe?g|gif)$/    
/\.(mp4|webm|ogg|mp3|wav)$/

手机号
/^(1|\+861)[3-8]{1}\d{9}$/

身份证
身份证号码为15位或者18位,15位为全数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X 
/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/

中文检测
/[\u4e00-\u9fa5]+/g

QQ号验证  
/^[1-9]\d{4,10}$/

座机号码
010-86123456-2345
023-67622000-02
0755-66608483
开头为0,后面是2或三位数字加上 - 开头为非零的八位数字,结尾是 - 加1到4位数字的分机号
(0\d{2,3}-)?[1-9]\d{7}(-\d{1,4})?

邮件格式
简单验证  /^\w+@[a-z0-9]+\.[a-z]+$/
具体验证  /^\w{2,18}@[0-9a-z]{1,10}(\.[a-z]{2,3}){1,2}$/

删除多余空格  
 str.replace(/\s+/,'');

删除首尾空格
str.replace(/^\s+/,'');
str.replace(/\s+$/,'');

P14 本地存储

HTTP协议

  • http:超文本传输协议
  • https:超文本传输安全协议
  • http协议的一个特点是无状态,同一个客户端的这次请求和上次请求没有对应关系。
  • 对http服务器来说,它并不知道这两个请求来自同一个客户端。
    为了解决这个问题, Web程序引入了cookie机制来维护状态。

cookie是什么

  • cookie是浏览器提供的一种机制,可以由JavaScript对其进行操作(设置、读取、删除)
  • cookie是一种会话跟踪技术,是存储于访问者计算机中的一小块数据
  • 会话:用户进入网站,开始浏览信息到关闭浏览器的过程,就称之为是一次会话
  • 会话跟踪技术:浏览器和服务器之间在进行多次请求间共享数据的过程,就称为会话跟踪技术

cookie的特性

  • cookie可以实现跨页面全局变量
  • cookie可以跨越同域名下的多个网页,但不能跨域使用
  • cookie会随着HTTP请求发送给服务器
  • cookie会存储于访问者的计算机中
  • 同一个网站中所有页面共享一套cookie
  • 可以设置有效期限
  • 存储空间为4KB左右

cookie应用场景

  • 会话状态管理(如用户登录状态、购物车等)
  • 个性化设置(保存用户设置的样式等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

cookie的缺点

  • cookie可能被禁用
  • cookie与浏览器相关,不能互相访问
  • cookie可能被用户删除
  • cookie安全性不够高
  • cookie会随着HTTP请求发送给服务器
  • cookie存储空间很小(只有4KB左右)
  • cookie操作麻烦,没有方便的API

操作cookie
每个cookie都是一个键/值对格式的字符串(key=value)

设置cookie:
document.cookie="user1=xh";
document.cookie="user2=xm";
如果要改变一个cookie的值,只能重新赋值

设置有效期:
var d=new Date();
d.setDate(d.getDate()+3); //按天数设置
document.cookie="user3=xd; expires="+d;

读取cookie:

var cookies = document.cookie;

只能够一次获取所有的cookie值
使用时必须自己解析这个字符串,来获取指定的cookie值

删除cookie:

// cookie过期会自动消失
// 要删除一个cookie,可将其有效期设为一个过去的时间
var d=new Date();
d.setDate(d.getDate()-1);
document.cookie="user1=xh; expires="+d;

HTML5本地存储

H5本地存储有 localStoragesessionStorage 两种

优点:

  • 更大的存储空间,有5MB左右
  • 不会随HTTP请求发送给服务器
  • 有方便的API操作
  • 移动端普及高

localStorage 为永久性保存数据,不会随着浏览器的关闭而消失。

按域名进行存储,可以在同域名下跨页面访问,不会和其他域名冲突。

按键值对存储:key/value

操作:

  • localStorage.setItem(key , value) 保存或设置数据,如果key已经存在,则覆盖key对应的value,如果不存在则添加key与value

  • localStorage.getItem(key) 获取key对应的value
    如果key不存在则返回null

  • localStorage.key(index) 获取指定下标位置的key

  • localStorage.length 获取数据条数(长度),配合key(index)方法可以实现遍历localStorage数据的方法

  • localStorage.clear() 将同域名下的所有数据都清空

  • localStorage.removeItem(‘key’) 删除某个键值对

sessionStorage 为临时性保存数据,当页面关闭就会消失

sessionStorage 不能跨页面访问,只局限在当前的标签页

sessionStorage 各种操作与 localStorage 一样

JSON

目前 JavaScript 使用非常多的 json 格式

可以使用 JSON.stringify() 将 json对象 转为 json字符串
然后把 json字符串 存储在 cookielocalStorage 里面

读取出来以后使用 JSON.parse() 将 json字符串 转为 json对象

示例:

var jsonObj = {"name1":"jack","name2":"lily"};
localStorage.setItem("user",JSON.stringify(jsonObj)); // 存储

var jsonObj = JSON.parse(localStorage.getItem("user")); // 读取

P15 闭包、原型链和继承

闭包(closure)

闭包的概念
官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

  • 闭包是指有权访问另一个函数作用域中的变量的函数
  • 闭包就是能够读取其他函数内部变量的函数
  • 闭包可以理解成定义在一个函数内部的函数
  • 函数就是闭包

当一个函数能够记住并访问到其所在的词法作用域及作用域链,特别强调是在其定义的作用域外进行的访问,此时该函数和其上层执行上下文共同构成闭包。

需要明确的几点

  1. 闭包一定是函数对象
  2. 函数内保持对上层作用域的引用
  3. 闭包和词法作用域、作用域链、垃圾回收机制等息息相关
  4. 当函数在其定义的作用域外进行访问时,才产生闭包
  5. 闭包是由该函数和其上层执行上下文共同构成

变量及作用域

变量无非就是两种:全局变量和局部变量。

Javascript语言中,函数内部可以直接读取全局变量,在函数外部无法直接读取函数内的局部变量。

程序设计中作用域的概念
通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

词法作用域

词法作用域,也叫静态作用域,它的作用域是指在词法分析阶段就确定了,不会改变。

动态作用域,是在运行时根据程序的流程信息来动态确定的,而不是在写代码时进行静态确定的。

主要区别:词法作用域是在写代码或者定义时确定的,而动态作用域是在运行时确定的。

词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。

javascript 使用的是词法作用域

// 词法作用域
var abc = 1;
function f1() {
    console.log(abc);
}
function f2() {
    var abc = 2;
    f1();
}
f2();

// 类似动态作用域
function show() {
    console.log(this);
}
show();
document.querySeletor(".btn").onclick = function () {
    console.log(this);
    show();
}
document.querySelector(".btn").onclick = show;
var timer=setTimeout(show,1000);

作用域链

作用域链:本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

每个执行环境都有一个与之关联的变量对象,执行环境中定义的所有变量和函数都保存在这个变量对象中。

全局执行环境是最外围的一个执行环境,在Web浏览器中,全局执行环境的变量对象是window对象。

当JavaScript解释器初始化执行代码时,首先默认进入全局执行环境。

  • 局部执行环境的变量对象,则只在函数执行的过程中存在。
  • 当函数被调用的时候,会创建一个特殊的对象–活动对象。
  • 活动对象之后会作为局部执行环境的变量对象来使用。
function compare(value1,value2){
    if(value1 < value2){
        return -1;
    } else if( value1 > value2 ) {
        return 1;
    } else {
        return 0;
    }
}
var result = compare(5, 10);

image

垃圾回收机制
各大浏览器通常采用的垃圾回收有两种方法:标记清除、引用计数

标记清除
当变量进入执行环境时,将这个变量标记为“进入环境”。当变量离开执行环境时,则将其标记为“离开环境”,就销毁回收内存。

引用计数
跟踪记录每个值被引用的次数,当引用次数变成0时,就销毁回收内存

function fn1(){
    var n = 5;
    n++;
    return n;
}
console.log( fn1() ); 
console.log( fn1() ); 

闭包的作用

闭包最大用处有两个:在函数外可以读取函数内部的变量;让这些变量的值始终保持在内存中。

function fn1(){
    var n = 5;
    return function fn2() {
        n++;
        return n;
    }
}
var fn = fn1();
console.log( fn() );
console.log( fn() );
console.log( fn() );

注意:
闭包会使得函数中的变量被保存在内存中,增加内存消耗,不能滥用闭包,否则会造成网页的性能问题,在低版本IE中还可能导致内存泄露。

原型及原型链

JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言。

在所有语言中,JavaScript 几乎是独一无二的,也许是唯一的可以被称为“面向对象”的语言,因为可以根本没有类而直接创建对象的语言很少,而 JavaScript 就是其中之一。

在 JavaScript 中,类不能(因为根本不存在)描述对象可以做什么,对象直接定义它自己的行为。

JavaScript 只有 对象。

我们把JS对象分为 普通对象 和 函数对象

属性:prototype(原型)
每个函数对象(Function.prototype除外)都有一个prototype属性,这个属性指向一个对象即 原型对象

var fn1 = function (){ };
var fn2 = new Function();
function fn3(){ };
console.log(fn1.prototype);
console.log(fn2.prototype);
console.log(fn3.prototype); // Object{} 这就是我们所说的原型,它是一个对象也叫原型对象

// 为什么说 Function.prototype 除外呢?看代码:
console.log(Number.prototype);
console.log(String.prototype);
console.log(Function.prototype);
console.log(Function.prototype.prototype);// 结果看下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WSXxlJWW-1587348864384)(http://csdn.raomaiping.xyz/o_200327082615Image.jpg)]
可以看到内置构造函数Number、String等,它们的原型指向一个普通对象(Number{}和String{}),而Function的原型则指向函数对象 function () { [native code] },就是原生代码,二进制编译的!这个函数对象(Function.prototype)是没有原型属性的,所以它的prototype返回 undefined。

我们继续来了解

function Cat(){};
Cat.prototype.name = '小白'; // 给原型对象添加属性
Cat.prototype.color = 'black'; // 给原型对象添加属性
Cat.prototype.sayHello = function (){ // 给原型对象添加方法
   console.log('大家好,我的名字叫'+this.name);
}
var cat1 = new Cat(); // 实例对象
var obj = Cat.prototype;// 原型对象
console.log(obj);
console.log(cat1.constructor);
console.log(obj.constructor);
console.log(Cat.prototype === cat1.constructor.prototype);

属性:constructor(构造器)
每个对象都有一个隐藏属性constructor,该属性指向对象的构造函数(“类”)

通过上面的代码我们可以看到,实例对象 cat1 和原型对象 obj 它们的构造器相同,都指向 Cat!

我们换一种写法:

function Cat(){}
Cat.prototype = {// 原型对象
   name: '小白',
   color: 'black',
   sayHello: function (){
       console.log('大家好,我的名字叫'+this.name);
   }
}
var cat1 = new Cat();

这种写法更直观看到原型对象是什么,但是

console.log(Cat.prototype === cat1.constructor.prototype); 
console.log(Cat.prototype.constructor === Object); 
console.log(cat1.constructor === Object); 

此时 Cat.prototype 指向一个对象字面量方式定义的对象{},其构造器(constructor)指向的自然是根构造器 Object,所以 cat1 的构造器也指向根构造器 Object。

由此可见,属性 constructor 并不可靠!

那么,原型有什么用呢?

原型的主要作用是用于“继承”

var Person = function(name){
   this.name = name;
};
Person.prototype.type = 'human';
Person.prototype.getName = function(){
   console.log(this.name);
}
var p1 = new Person('jack');
var p2 = new Person('lucy');
p1.getName();
console.log(p1.type);
p2.getName();
console.log(p2.type); 

示例中通过给原型对象(Person.prototype)添加属性方法

那么由 Person 实例出来的普通对象(p1 p2)就继承了这个属性方法(type getName)

再看一个示例

Object.prototype.jdk = 'abc123';
Object.prototype.sayHi = function (){
   console.log('嗨~大家好');
}
String.prototype.pin = function (){
   console.log(this+'&biubiu');
}
var str = 'yoyo';
var num = 666;
var arr = [];
var boo = true;

str.sayHi(); // 嗨~大家好
num.sayHi(); // 嗨~大家好
arr.sayHi(); // 嗨~大家好
boo.sayHi(); // 嗨~大家好
console.log(str.jdk); // abc123
console.log(num.jdk); // abc123
console.log(arr.jdk); // abc123
console.log(boo.jdk); // abc123
str.pin(); // yoyo&biubiu
num.pin(); // 报错 num.pin is not a function
arr.pin(); // 报错 arr.pin is not a function
boo.pin(); // 报错 boo.pin is not a function

看出点什么了吗?

所有对象都继承了Object.prototype原型上的属性方法(换句话说它们都是Object的实例)

str 还继承了String.prototype原型上的属性方法

再看之前写过的示例:

Date.prototype.getWeek = function () {
   var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
   var index = this.getDay();//0-6
   return arr[index];
}
var dates = new Date();
console.log(dates.getWeek()); // '星期一'

所有 Date 对象都将继承 getWeek 方法

具体是怎么实现的继承,我们就要讲到原型链了

属性:_ _ proto _ (原型)

每个对象都有一个隐藏属性_ _proto_ _,用于指向创建它的构造函数的原型
懵逼......怎么又一个原型???

上面我们讲prototype是针对每个函数对象,这个_ _ proto _ _是针对每个对象
属性_ _ proto _ _非官方标准属性,但主流的浏览器基本都支持
var n = 123;
var s = 'jdk';
var b = true;
var a = [];
var f = function (){};
var o = {};

console.log(n.__proto__);
console.log(n.__proto__ === Number.prototype);

console.log(s.__proto__ === String.prototype);
console.log(a.__proto__ === Array.prototype);
console.log(f.__proto__ === Function.prototype);
console.log(o.__proto__ === Object.prototype);
console.log(b.__proto__ === Boolean.prototype);

对象 通过_ _ proto _ _指向原型对象,函数对象 通过prototype指向原型对象
那么原型链呢,链在哪?

通过上面写的示例,我们来找找原型链:

Object.prototype.jdk = 'abc123';
Object.prototype.sayHi = function (){
   console.log('嗨~大家好');
}
var str = 'yoyo';
str.sayHi(); // 嗨~大家好
console.log(str.jdk); // 'abc123'

str 是怎么访问到 sayHi 方法和 jdk 属性的呢?

了解一下方法 hasOwnProperty() ,用于判断某个属性是否为该对象本身的一个成员

看看大致的访问过程:

console.log(str.hasOwnProperty('sayHi'));//false str自身没有sayHi方法
console.log(str.__proto__.hasOwnProperty('sayHi'));//false 原型对象也没有sayHi方法
console.log(str.__proto__.__proto__.hasOwnProperty('sayHi'));//true 原型的原型有sayHi方法

str -> str._ _ proto _ _ -> str._ _ proto _ _ . _ _ proto _ _ 感觉到什么吗?

我们来描述一下执行过程:

str.sayHi() --> 自身查找 --> 没有sayHi方法 --> 查找上层原型 str._ _ proto _ _ --> 指向 String.prototype对象 --> 没有sayHi方法 --> 查找上层原型 String.prototype._ _ proto _ _ --> 指向Object.prototype对象 --> 找到sayHi方法 --> 执行sayHi方法

环环相扣,是不是像链条一样呢?这个就是我们所说的 原型链

熟悉了原型和原型链,我们来看看JS中常见实现“继承”的方式

// demo1 对象冒充继承
function Cat(n,c){ // 猫 类
   this.name = n;
   this.color = c;
   this.trait = function (){
       console.log('卖萌~');
   }
}
Cat.prototype.skill = function (){ // 原型上的属性方法
   console.log('抓老鼠');
}

// 需求:狗要卖萌,狗要多管闲事-抓老鼠
function Dog(n,c,f){ // 狗 类
   this.food = f;
   Cat.call(this,n,c); // 狗冒充猫,访问猫的属性方法
}
var dog1 = new Dog('二哈','yellow','shi');// 实例对象
console.log(dog1.name); // 二哈
dog1.trait(); // 卖萌
dog1.skill(); // 报错 dog1.skill is not a function

我们看到这种继承方式有局限性,“父类”原型上的属性方法无法继承,所以二哈没有抓老鼠的技能

// demo2 原型链继承
function Cat(n,c){ // 猫 类
   this.name = n;
   this.color = c;
   this.trait = function (){
       console.log('卖萌~');
   }
}
Cat.prototype.skill = function (){// 原型上的属性方法
   console.log('抓老鼠');
}

function Dog(n,c,f){ // 狗 类
   this.food = f;
}
Dog.prototype = new Cat(); // 把狗的原型指向猫的实例对象

var dog1 = new Dog('二哈','yellow','shi');
console.log(dog1.name); // undefined
console.log(dog1.food); // shi
dog1.trait(); // 卖萌~
dog1.skill(); // 抓老鼠
console.log(dog1.constructor); // Cat

问题一:实例化对象的时候不能给“父类”传参,导致访问dog1.name没有值

问题二:有句台词:‘人是人妈生的,妖是妖妈生的 ’ 现在 dog1.constructor 指向 Cat,意味着 二哈 是猫妈生的!很显然这不符合伦理,也不环保…

// demo3 混合继承
function Cat(n,c){
   this.name = n;
   this.color = c;
   this.trait = function (){
       console.log('卖萌~');
   }
}
Cat.prototype.skill = function (){
   console.log('抓老鼠');
}

function Dog(n,c,f){
   this.food = f;
   Cat.call(this,n,c);// 对象冒充继承
}

// Dog.prototype = new Cat(); “构造器调用”得到一个对象,容易产生一些副作用

Dog.prototype = Object.create(Cat.prototype);// 原型链继承
// Object.create()用于创建一个空对象,并把该对象的[[Prototype]]链接到Cat.prototype

Dog.prototype.constructor=Dog;// 指正构造器

var dog1=new Dog('二哈','yellow','shi');
console.log(dog1.name);// 二哈
console.log(dog1.food);// shi
dog1.trait();// 卖萌~
dog1.skill();// 抓老鼠
console.log(dog1.constructor);// Dog

两种方式结合可以实现相对比较完美的“继承”

别忘了指正构造器(类型),不能认贼作父!

小结:
在 JavaScript 中,没有类,只有对象
多年来JS开发者们努力尽可能地模拟面向类(山寨成某些看起来像“类”的东西),原型机制和“类”不一样,在面向类的语言中,可以制造一个类的多个 拷贝(即“实例”),但是在 JavaScript 中,没有这样的拷贝处理发生。

原型机制是一个内部链接,其本质是行为委托、对象间创建链接,这种链接在对一个对象进行属性/方法引用,而这样的属性/方法不存在时实施,在这种情况下,[[Prototype]] 链接告诉引擎在那个被链接的对象上查找这个属性/方法,接下来,如果这个对象不能满足查询,它的 [[Prototype]] 又会被查找,如此继续。。。
这个在对象间的一系列链接构成了所谓的“原形链”


  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值