JS特殊语法


[TOC]

event.preventDefault()方法

//    $0.onclick = (e)=>{e.preventDefault();alert(1);}
//    html A tag id : #aaa
var a = document.getElementById('aaa');
//    这里的参数e代表当前所触发的鼠标事件内的,鼠标信息,比如说当前鼠标所在屏幕、页面位置、点击的左键还是右键等等
a.onclick = function(e){
  //    取消一些DOM元素的默认事件,比如说A标签的链接跳转功能
  e.preventDefault();
}

event.stopPropagation()方法

//    假设box是个框框,当它点击的时候输出1111
var box = document.getElementById('box');
box.onclick = ()=>{
  console.log(1111)
}
//    给body一个点击事件,输出2222
document.querySelector('body').onclick = ()=>{console.log(2222)}

//    当点击了#box之后,实际上会输出两个内容:1111,2222
//    因为,当儿子元素响应事件之后,还会传递到它的父亲,所以body也会输出内容。
//    当使用event.stopPropagation()方法之后,则不会传递到上一层内容之中。

box.onclick = (e)=>{
  e.stopPropagation();
  console.log(1111)
}

event.target什么意思

比如我们有两个按钮

按钮A 按钮B

当你给按钮A制作一个click事件,并点击它的时候,如你所见,target就是self,因为是自己的事件内出发的该事件。

可是呢,当你在按钮A的click事件内,主动去调用按钮B的点击事件,此时,按钮B的click也会被出发,不过,target就不是自己啦。

a.onclick = function(e){
//  这里如果我主动点击,那么e.target就是我自己。
    console.log(111);
//    是不是就变了?所以b的click内的e:event的target就不是自己了,会显示是a元素调用的
  b.click();
}
b.onclick = function(e){
  //    但是a调用了b内的点击事件,那么e.target就不是b自己了,而是a触发的。
  console.log(222);
}

oncontextmenu

这个是鼠标右键的默认菜单,想要屏蔽它,需要将当前元素取消默认的oncontextmenu返回值就OK了

box.oncontextmenu = function(e){
  return false;
}

box.oncontextmenu = e => false

onselectstart

可以不让别人选择文本

box.onselectstart = e => false

oncopy

可以禁止复制文本

box.oncopy = e => false

::selection

可以控制选择的文本背景色和被选中的文字颜色

::selection
{
    color:#ff0000;
    background: #088;
}

如何制作鼠标右键的菜单

1、建立HTML,弹出的鼠标菜单选项(position:absolute;z-index:9999;background:#000)
2、你需要屏蔽掉用于弹出菜单的层的鼠标右键:oncontextmenu = e => false
3、在用于弹出菜单的层,右键事件中,对准备好的弹出菜单显示,位置用鼠标事件中的e.pageXe.pageY来决定弹出菜单的top和left的样式。

a6 => <div id="a6" style="display:none; position: absolute; background: rgb(0, 0, 0); z-index: 9999;"><ul><li>我们的祖国</li><li></li><li>我们的祖国</li><li></li><li>我们的祖国</li><li></li></ul></div>

a6 = document.getElementById('a6');
box.onmousedown = e =>{ if(e.button == 2){ a6.style.left = e.pageX + 'px'; a6.style.top = e.pageY + 'px';a6.style.display:block }}

其他内容


1、闭包

闭包就是前置执行,在方法体前和方法体后,加入一组小括号,代表成为一个整体,该整体就叫做闭包,用于分离开变量的作用域。

(function(){});
//    如何调用呢?

(function(){})();

var abc = (function(){ return 123;})();

闭包下的作用域,返回JSON/Object类型

var obj = (function(a,b,c){
  
  //    private members
  
  return {
    //    public members;
  }
})(1,2,3);

返回Function类型,返回的一个基于Function类型的对象(可以理解为类)

var Book = (function(a,b,c){
  
  var age = 16;
  this.name = 111;
  
  return function(){
    
      //    private members
    var a1,a2,a3;
    
    //    public members;
    this.isbn = "N12309128398";
  }
})(1,2,3);

var b = new Book();
b.isbn; //    N12309128398
var Dog = function(){
    
      //    private members
    var a1,a2,a3;
    
    //    public members;
      
      this.getAge = function(){
      return 123;
    }
  
}

var d = new Dog();
d.getAge();    //    123

2、JS变态写法

||或者运算:

var b = undefined;
var a = b || 123;
//    现在a的值是123

为什么呢?注意,在JS中,或者||可以判断左侧的变量是否为null或者undefined,如果是的话,就取右侧的值,赋值给左侧变量。

例子:

function abc(a,b){
    a = a || 123;
    console.log(a);
}

abc();
//    123

&&与运算:

var b = true;
b && console.log(123);

//    结果:123

如果&&与符号前面表达式为真,那么后面就会被执行。相反如果前面为假,后面就不会被执行。

连写:

var username="admin", password = "admin";
if(username == "admin" && password == "admin"){
    alert("登录成功")
}

注意:var声明变量的这一行,必须用分号结尾之后才可以执行自定义语句,不能写在一行。

使用三元运算符,可以区分逻辑再执行

var username="admin", password = "admin";
(username == "admin" && password == "admin") ? alert("登录成功") : alert("登录失败");

注意:括号内的逗号,可以隔开多条待执行语句

var username="admin", password = "admin";
(username == "admin" && password == "admin") ? (alert("登录成功"), location.href = "http://www.baidu.com") : alert("登录失败");

forEach遍历

注意:forEach在JS中,是异步的,不是同步,for循环才是同步的。
也就是说,forEach的后面的语句,不会等待forEach执行完毕后再去执行。

var arr = [1,2,3,4,5];
//    for的做法
for(var i = 0; i < arr.length; i++){
  arr[i]++;    // arr[i] += 1;
}
//    这个会等待for语句执行完毕后,会打印111
console.log(1111);


//    forEach的做法,它是不能有返回值的
arr.forEach((item, index) => {
  //    在这里修改item的值,只能在当前方法大括号内被改变,不会影响到arr数组内的值
  item += 100000;
})

//    这里的1111,不一定在forEach执行完毕后才被打印,所以forEach是异步的,非同步。
console.log(1111);

Map

它是数组的拓展方法,意思是将数组遍历一次,并将遍历时,元素计算后的结果返回。

var arr = [1,2,3,4,5];
for(var i = 0; i < arr.length; i++){
  arr[i]++;    // arr[i] += 1;
}

//    Map
//    Map最需要注意的是返回值,必须要return
//    要求:它不是过滤用的,必须每一个元素都需要返回。
var arr2 = arr.map((item, index) => {
    item += 100;
  return item    // return当前元素你要干什么之后改变的值
})

//    arr2 => [101,102,103,104,105]

Filter过滤器

它是数组的过滤器,如果你感觉某个元素不符合你的要求,可以将它排除。

var arr = [1,2,3,4,5];

for(var i = 0; i < arr.length; i++){
  if(i < 3){
    arr.splice(i,1)
  }
}

//    filter的做法
var arr2 = arr.filter((item, index) => {
    
  //    你这里需要返回,你认为达到标准的元素,当表达式为真则返回该元素。
  return item    > 3;
})

3、ES6

难度较大:

1、数组解构、对象解构
2、函数的rest参数
3、数组的拓展运算符
4、对象的拓展运算符

比较简单:

5、Set 和 Map 数据结构
6、Iterator 和 for...of 循环

难度较大:

7、Promise 对象(工作常用)
8、async 函数(工作常用)
9、Class 的基本语法
10、Class 的继承

Promise

非同步,异步,异步的东西转变为同步才使用await/async。
网络请求的地方使用await,只要你使用了await,就必须在当前的方法外侧声明为async。

只有返回值为promise对象的方法才可以使用await

所以,必须会Promise。

//    取得用户详情信息
function getProfile(){
  
  //    这里去请求网络...
  //    在返回值的时候,你也不知道网络请求成功了没有,所以你就返回了一个带有没有的状态。这个状态就是promise。
  //    return new Promise(这里啊是个回调函数)
  return new Promise((resolve,reject) => {
    //    resolve是执行成功了,reject是失败了
    resolve(data);    //    比如说data就是网络请求的数据。
  });
  
  //    所以promise其实就是一个等待数据来的函数,但是数据很可能还没来呢。
}
//    不使用await需要使用then,来获取数据到达
getProfile().then( res => {
    //    res就相当于返回的data数据,但是注意,这里是resolve来的地方
}, error => {
  //    这里是reject来的地方。所以就发生了错误
})

//    这里啊还可以使用catch方法截获错误,那就不用使用reject的回调函数了
getProfile().then( res => {
  
}).catch(err => {
  
})
//    既然使用await,就必须加上async,才能用,不然报错
async function wrapProfile(){
  //    你的data肯定是resolve来的数据,但是请问reject在哪?
  let data = await getProfile();
  
  //    最好的办法是使用try-catch语法来解决这样的错误和问题
  try{
    let data = await getProfile();
    //    所以我们在这里进行判断data是不是null就可以了
        if(data != null){
        //  就行了
    }
  }catch(error){
    //    一旦出现错误就会来到这里
    //    在这里可以处理错误信息
  }
}

Promise.all()

就是当多个含有promise的对象都处理完成后才会进入回调函数。

Promise.any()

就是数组内多个含有promise的对象,只要有一个处理完成,就会进入回调函数。

Set

set就相当于一个没有重复元素的数组,只不过添加数据的方式是使用add,而不是push。

let s = new Set();

s.add(1).add(2).add(2);
// 注意2被加入了两次

s.size // 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2);
s.has(2) // false

Map

键值对。

key => value,相当于PHP中的array。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

4、JS基础语法拓展

call、apply、bind的作用于区别

方法内的this默认指向为Window

call是改变方法内的this指向的目标,且立即执行。
apply是改变方法内的this指向的目标,且立即执行,和call的区别是第二个参数为一个数组。
bind是改变方法内的this指向的目标,但是不会立即执行,需要自己调用才能执行。

function get(){
    //  this => Window
  //    当被改变之后 this => set
  this.setTimeout(function(){console.log(111);},1500);
}

function set(){
  this.setTimeout = function(){
    console.log("小强")
  }
}

var s = new set();

//    默认
get();

//    call和apply区别:
get.call(s,1,2,3,4);
get.apply(s,[1,2,3,4]);

//    委托/代理
var proxy = get.bind(s,1,2,3,4);
proxy();

//    打印的结果:

//    小强
//    小强
//    小强
//    undefined
//    111

prototype是什么

//    我们定义一个Dog类,想要让它有个叫做say的成员方法

//    第一种实现方法
function Dog(){
    this.say = function(){
      console.log("汪汪汪")
    }
}

//    new Dog().say();

//    第二种实现方法
function Dog(){
  
}

//    可以使用prototype,在类的原型链中动态添加新方法
//    也被可以称之为:拓展方法
Dog.prototype.say = function(){
  console.log("汪汪汪")
}

//    new Dog().say();

for..in和for..of的区别

for..in会取出对象、类中的原型链上的内容,而for..of则不会。
for..in可以遍历任何类、对象,但是for..of只能遍历实现了iterator遍历器实现的类、对象。

for...in 循环:只能获得对象的键名,不能获得键值,而for...of 循环:允许遍历获得键值

//    arr为数组,则是Array的对象
let arr = [1, 2, 3];

for(let x in arr){
    console.log(x);
}

//    1
//    2
//    3

//    在原有的Array类上添加一个方法,就是在Array的原型链上进行拓展
Array.prototype.something = function(){
  
}

for(let x in arr){
    console.log(x);
}

//    1
//    2
//    3
//    something


//    注意,for..of方法,它不会取得对象原型链上的内容
for(let x of arr){
    console.log(x);
}

//    1
//    2
//    3

//    所以我们认为,for..of是相对于for..in来说较为安全的。
//    因为for..in会打印出来一些我们并不需要的内容,
//    但是for..of需要对象实现了iterator接口。

总之,for...in 循环主要是为了遍历对象而生,不适用于遍历数组
for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象

浅拷贝:值传递和引用传递

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

//    b = ?
b = 1
//    为什么呢?因为你要赋值的操作是值类型的(数字,字符串,布尔),是直接拷贝(复制)过来。

var a = [1,2,3,4,5], b = a;
b[0] = 99;

//    a = ?
//    a = [99,2,3,4,5]

//    为什么呢?因为你要赋值的操作是引用类型的(基于对象),所以是取得它的地址而交给赋值左侧的变量。

添加新评论