目录
JS可以用来做什么?
JS在前端中几种写法:
1. 文件引用:
2. 页面样式
3. 行内样式
集中常见的弹框:
JS基本语法:
变量:
常量:
数据类型:
基本数据类型:
引用数据类型:
数据类型转换:
运算符:
算术运算符
逻辑运算符:
比较运算符:
赋值运算符:
自增自减运算符:
三目运算符:
流程控制:
分支结构:
单分支:
双分支:
多分支:
switch语句:
循环结构:
while 循环:
for 循环:
do...while 循环:
break 语句和 continue 语句:
label 标签:
JS中this指向详解:
什么是this?
一、this的指向:
1.1. 全局环境中的this:
1.2. 对象中的this:
1.3. 构造函数中的this:
1.4. 事件中的this:
1.5. 箭头函数中的this:
1.6. 严格模式下的this:
二、改变this的指向(绑定this):
1.1. call方法:
1.2. apply方法:
1.3. bind方法:
1.4. 方法对比:
三、使用的注意点:
1.1. 避免多层 this:
1.2. 避免数组处理方法中的 this:
1.3. 避免回调函数中的 this:
JS可以用来做什么?
- 数据校验
- 网页特效
- 数据交互
JS在前端中几种写法:
1. 文件引用:
// main.html
<script src="js.js"> </script>
// js.js
alert("hello word")
注意:进行引用的script标签不能自结束,也不能在内容部分中写代码,要写代码另起一个script标签。
2. 页面样式
<script type="text/javascript">
alert('hello world!')
</script>
3. 行内样式
<button οnclick="alert('你点我干嘛?')">按钮</button>>
集中常见的弹框:
alert 警告框:
confirm 确认框:
两个按钮,一个取消一个确定,点击取消confirm将会返回false,点击确定返回True。
prompt输入框:
两个参数。
第一个参数是输入框提示信息,第二个参数是默认值。
当点击确定后会将用户输入的内容以字符串的形式返回。
JS基本语法:
变量:
- 使用let关键字进行对变量的声明 ;
- 使用var关键字进行对变量的声明 (可以重复创建变量,let不行);
常量:
值不会发生变化的量。
使用const 关键字创建常量。
数据类型:
基本数据类型:
- 数值类型Number
-
-
- 整型int
- 浮点型float
-
- 字符串类型String
被单引号,双引号或反引号引起来的内容就是字符串
- 布尔型Boolean
他只有两个值一个是True一个是False
- null表示
空类型
- NaN(not a number)
表示这不是一个数字
- undefined
表示未定义
引用数据类型:
数组Array:关于多个数据的容器
对象Object
数据类型转换:
- 自动类型转换:
- 强制类型转换:
字符串转为整型:num = parseInt(str)
字符串转为浮点型:num = parseFloat(str)
prompt函数返回的是字符串,即使输入的内容是数字,
所以需要使用parseInt、parseFloat进行类型强制转换;
运算符:
算术运算符
加+、减-、乘*、除÷、取余%、幂次**
逻辑运算符:
包括 与(&&、并且)、或(||、或者)、非(!、不是)三种
比较运算符:
>、<、>=、<=、 ==、===、!=
- 在JS中双等只判断值是否相等不判断类型;
- 三等会考虑变量的数据类型,如果数据类型不同,即使值一样也是返回false;
赋值运算符:
=、+=、-=、*=、\=、**=、 %=
自增自减运算符:
a++、++a、a--、--a
如若自增或自减符号写在变量后面,则他的运算优先级是最低的,低过等号,
也就说会先进行赋值运算在进行自增/自减;
a++ 先赋值再自增自减 ++a 先自增自减再赋值
三目运算符:
变量 = 表达式 ? 值 1: 值2; 表达式如果结果为true,则变量的值就是第一个值1,否则就是值2。
条件? 条件成立的代码:条件不成立的代码
流程控制:
分支结构:
单分支:
单分支主要就是针对一种情况下的判断,使用 if 关键字。
双分支:
多分支:
if语句的多分支
switch语句:
switch和if多分支的区别:
- 1.switch必须是一个具体值才可进行分支处理,他不能用不等式代替。
- 2.switch代码结构清晰,一目了然,但是最重要的是switch 使用了binary Tree(二叉树),
只需要以此计算就可以找到对应分支,所以分支较多的情况下switch执行效率高
循环结构:
while 循环:
While语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。
while (条件)
语句;
// 或者
while (条件) 语句;
for 循环:
for语句是循环命令的另一种形式,可以指定循环的起点、终点和终止条件。
for (初始化表达式; 条件; 递增表达式)
语句
// 或者、
for (初始化表达式; 条件; 递增表达式) {
语句
}
for语句后面的括号里面,有三个表达式:
- 初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。
- 条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。
- 递增表达式(increment):每轮循环的最后一个操作,通常用来递增循环变量。
do...while 循环:
do...while循环与while循环类似,唯一的区别就是先运行一次循环体,然后判断循环条件。
do
语句
while (条件);
// 或者
do {
语句
} while (条件);
不管条件是否为真,do...while循环至少运行一次,这是这种结构最大的特点。
另外,while语句后面的分号注意不要省略。
break 语句和 continue 语句:
break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行。
break语句用于跳出代码块或循环。
var i = 0;
while(i < 100) {
console.log('i 当前为:' + i);
i++;
if (i === 10) break;
}
上面代码只会执行10次循环,一旦i等于10,就会跳出循环。
for循环也可以使用break语句跳出循环。
for (var i = 0; i < 5; i++) {
console.log(i);
if (i === 3)
break;
}
// 0
// 1
// 2
// 3
上面代码执行到 i 等于3,就会跳出循环。
continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环。
var i = 0;
while (i < 100){
i++;
if (i % 2 === 0) continue;
console.log('i 当前为:' + i);
}
上面代码:
只有在 i 为奇数时,才会输出 i 的值;
如果 i 为偶数,则直接进入下一轮循环;
如果存在多重循环,
不带参数的break语句和continue语句都只针对最内层循环。
label 标签:
JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下:
label:
语句
标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。
标签通常与break语句和continue语句配合使用,跳出特定的循环。
top:
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (i === 1 && j === 1) break top;
console.log('i=' + i + ', j=' + j);
}
}
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
上面代码为一个双重循环区块, break 命令后面加上了 top 标签(注意, top 不用加引号),
满足条件时,直接跳出双层循环。如果 break 语句后面不使用标签,则只能跳出内层循环,
进入下一次的外层循环。
标签也可以用于跳出代码块。
foo: {
console.log(1);
break foo;
console.log('本行不会输出');
}
console.log(2);
// 1
// 2
上面代码执行到 break foo ,就会跳出区块。
continue语句也可以与标签配合使用。
top:
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (i === 1 && j === 1) continue top;
console.log('i=' + i + ', j=' + j);
}
}
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// i=2, j=0
// i=2, j=1
// i=2, j=2
上面代码中, continue 命令后面有一个标签名,
满足条件时,会跳过当前循环,直接进入下一轮外层循环。
如果 continue 语句后面不使用标签,则只能进入下一轮的内层循环。
JS中this指向详解:
什么是this?
this是Javascript语言中的一个关键字,它代表函数运行时自动生成的一个内部对象,指当前对象,因此
this只能在函数内部使用。随着函数执行环境的改变,this所指代的对象也会发生改变,
但this始终指向的是调用函数的对象。简单来说就是谁调用了这个函数,this就指向谁(对象)。
一、this的指向:
1.1. 全局环境中的this:
所谓全局环境是指在script标签的内部,而全局环境下的作用域就是全局作用域。
全局作用域下的this始终指向的是全局对象window。
(window是浏览器对象中最顶层的节点)
-
-
- 全局环境中直接打印this:
-
<script>
console.log(this)
</script>
该this指向window:
-
-
- 全局中调用普通函数:
-
<script>
function fn() {
console.log(this)
}
fn()
</script>
该this指向window:
-
-
- 全局中调用定时器和延时器函数:
-
<script>
setTimeout(function () {
console.log(this)
}, 1000)
setInterval(function () {
console.log(this)
}, 10000)
</script>
该this指向window:
1.2. 对象中的this:
对象内部方法的this指向调用这些方法的对象,即由哪个对象调用就指向哪个对象,
如果是多层嵌套的情况,内部方法的this指向离被调用函数最近的对象
(window也是对象,其内部对象调用方法的this指向内部对象,而不是window)
<script>
let obj-1 = {
fn-1: function () {
console.log(this)
},
obj-2: {
fn-2: function () {
console.log(this)
},
},
}
obj-1.fn-1()
obj-1.obj-2.fn-2()
</script>
//fn-1由obj-1调用,故this指向obj-1
//obj-2离的更近,fn-2由obj-2调用,故this指向obj-2
1.3. 构造函数中的this:
构造函数中的this与被构造函数实例出来的新对象绑定,即指向该新对象。
<script>
let Person = function (name, age) {
this.name = name
this.age = age
console.log(this)
}
let kun = new Person('小黑子', 666)
</script>
该this指向由构造函数 Person实例出来的对象 kun;
1.4. 事件中的this:
在HTML事件句柄中,this指向的是触发该事件的HTML元素对象,即指向事件源。
<button id="btn" onclick = "this.style.display='none'">点击删除</button>
<script>
const btn = document.getElementById("btn")
btn.onclick = function () {
this.value = "按钮"
console.log(this)
}
</script>
//给btn注册了on事件,因此该this指向btn
1.5. 箭头函数中的this:
普通函数内部的this指向函数运行时所在的对象,但ES6中新增的箭头函数并没有自己的this对象,
所以只能指向上一层作用域(父级作用域)中的this。
也就是说,箭头函数内部的this指向是固定的。
<script>
var c = 21 //声明同一个全局变量 c 为21
const obj = {
c: 42,
x: () => console.log(this.c),
k: function () {
console.log(this.c)
},
}
obj.x() //箭头函数中的 this指向父级作用域中声明的变量 c,即为21
obj.k() //obj调用函数 k,函数 k中的 this指向 obj中声明的变量 c,即为 42
</script>
//箭头函数中的 this指向父级作用域中声明的变量 c,即为21
//obj调用函数 k,函数 k中的 this指向 obj中声明的变量 c,即为 42
1.6. 严格模式下的this:
JavaScript除了提供正常模式外,还提供了严格模式(strict mode)。
ES5 的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行 JS 代码,
可消除Javascript语法中的不合理、不严谨之处,减少一些怪异行为,保证代码运行的安全并且可以提高编译器的效率。
严格模式下全局调用普通函数,其this指向为undefined,其他情况与正常模式相同。
<script>
"use strict"
function fn() {
console.log(this) //该this指向undefined
}
fn()
</script>
//该this指向undefined
二、改变this的指向(绑定this):
1.1. call方法:
function.call( thisArg,arg1, arg2,arg3······ )//立即执行函数
function: 需要改变this指向的原函数
thisArg: 改变后的this所指向的新目标对象
arg:需要修改的参数----> 参数用逗号隔开即可;
<script>
function fn(name, age) {
this.name = name
this.age = age
console.log(this)
}
const obj = {}
fn.call(obj, "小黑子", 666) //函数fn中的this指向空对象obj并具备name和age属性
</script>
//函数fn中的this指向空对象obj并具备name和age属性
1.2. apply方法:
function.apply( thisArg,[arg1, arg2,arg3······] )//立即执行函数
function: 需要改变this指向的原函数
thisArg: 改变后的this所指向的新目标对象
[arg]:一个数组,包含需要修改的参数
<script>
function fn(name, age) {
this.name = name
this.age = age
console.log(this)
}
const obj = {}
fn.apply(obj, ["小黑子",666]) //函数fn中的this指向空对象obj并具备name和age属性
</script>
//函数fn中的this指向空对象obj并具备name和age属性
应用:
(1)找出数组最大元素
结合使用 apply 方法和 Math.max 方法,就可以返回数组的最大元素。
var a = [10, 2, 4, 15, 9];
let Mathmax=Math.max.apply(null, a)
console.log(Mathmax)
// 15
(2)将数组的空元素变为 undefined
通过 apply 方法,利用 Array 构造函数将数组的空元素变成 undefined 。
let a=Array.apply(null, ['a', ,'b'])
console.log(a)
// [ 'a', undefined, 'b' ]
空元素与 undefined 的差别在于,数组的 forEach 方法会跳过空元素,
但是不会跳过 undefined 。因此,遍历内部元素的时候,会得到不同的结果。
var a = ['a', , 'b'];
function print(i) {
console.log(i);
}
a.forEach(print)
// a
// b
Array.apply(null, a).forEach(print)
// a
// undefined
// b
(3)转换类似数组的对象
利用数组对象的 slice 方法,可以将一个类似数组的对象(比如 arguments 对象)转为真正的数组
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
上面代码的 apply 方法的参数都是对象,但是返回结果都是数组,这就起到了将对象转成数组的目的。
从上面代码可以看到,这个方法起作用的前提是,
被处理的对象必须有 length 属性,以及相对应的数字键。
(4)绑定回调函数的对象
var o = new Object();
o.f = function () {
console.log(this === o);
}
var f = function (){
o.f.apply(o);
// 或者 o.f.call(o);
};
// jQuery 的写法
$('#button').on('click', f);
上面代码中,点击按钮以后,控制台将会显示 true 。
由于 apply() 方法(或者 call() 方法)不仅绑定函数执行时所在的对象,
还会立即执行函数,因此不得不把绑定语句写在一个函数体内。
更简洁的写法是采用下面介绍的 bind() 方法。
1.3. bind方法:
function.bind( thisArg,arg1, arg2,arg3······ )//不会立即执行函数
function: 需要改变this指向的原函数
thisArg: 改变后的this所指向的新目标对象
arg:需要修改的参数
该方法并不会调用函数,仅仅是改变this指向,相当于返回一份修改了this的函数的拷贝
<script>
function fn(name, age) {
this.name = name
this.age = age
console.log(this)
}
const obj = {}
//函数被借用时,不会立即执行,而是返回一个新的函数
//所以需要自己手动调用新的函数来改变this指向
const newFn = fn.bind(obj, '小黑子', 666) //this指向空对象obj并具备name和age属性
newFn()
</script>
//函数被借用时,不会立即执行,而是返回一个新的函数
//所以需要自己手动调用新的函数来改变this指向
//this指向空对象obj并具备name和age属性
bind()方法有一些使用注意点:
(1)每一次返回一个新函数;
(2)结合回调函数使用;
(3)结合call()方法使用;
1.4. 方法对比:
call | 既可以调用函数又可以传参数,参数只需用逗号隔开 |
apply | 既可以调用函数又可以传参数,参数需要用一个数组进行包裹 |
bind | 不可以调用函数,可以传参数,参数只需用逗号隔开,返回得到一个新的函数,执行需要再次调用 |
三、使用的注意点:
1.1. 避免多层 this:
由于this的指向是不确定的,所以切勿在函数中包含多层的this。
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
o.f1()
// Object
// Window
上面代码包含两层this,结果运行后,第一层指向对象o,第二层指向全局对象;
实际上执行了下面代码:
解决办法:第二层改用一个指向外层this的变量。
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
上面代码定义了变量that,固定指向外层的this,然后在内层使用that,就不会发生this指向的改变。
事实上,使用一个变量固定this的值,然后内层函数调用这个变量;
1.2. 避免数组处理方法中的 this:
数组的map和foreach方法,允许提供一个函数作为参数。
这个函数内部不应该使用this。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
});
}
}
o.f()
上面代码中,foreach方法的回调函数中的this,其实是指向window对象,因此取不到o.v的值。原因跟上一段的多层this是一样的,就是内层的this不指向外部,而指向顶层对象。
解决办法一:使用中间变量固定this。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(that.v+' '+item);
});
}
}
o.f()
解决办法二:将this当作foreach方法的第二个参数,固定它的运行环境。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
}, this);
}
}
o.f()
1.3. 避免回调函数中的 this:
回调函数中的this往往会改变指向,最好避免使用。
var o = new Object();
o.f = function () {
console.log(this === o);
}
// jQuery 的写法
$('#button').on('click', o.f);
上面代码中,点击按钮以后,控制台会显示false。
原因是此时this不再指向o对象,而是指向按钮的 DOM 对象,因为f方法是在按钮对象的环境中被调用的。这种细微的差别,很容易在编程中忽视,导致难以察觉的错误;
为了解决这个问题,可以采用下面的一些方法对
this
进行绑定,也就是使得
this
固定指向某个对象,减少不确定性。