Web前端JS-02-JS基础语法

万丈高楼,始于垒土,所以基础语法就是我们技术的根基,今天我们来聊聊JS的基础语法
温馨提示:文章结构如下,阅读完可能需要花费5分钟

一、JS的数据类型
二、变量
三、内置对象
tmpdir__17_9_7_15_04_07.jpg

进入正文

一、JS的数据类型

数据类型可分为2类:基本数据类型与引用数据类型
基本数据类型包括:Undefined、Null、String、Bool、Number、Symbol

1.1基本数据类型的特点

  • 值不可改变
  • 存放在栈中

1.2 Undefined

特点 : 变量原始值、函数默认返回值、访问对象上不存在的属性

1.3 Null - 空对象

知识点1: 我们的内存地址可以识别数据类型
000 - 对象,数据是对象的应用
1 - 整型,数据是31位带符号整数
010 - 双精度类型,数据是双精度数字
100 - 字符串,数据是字符串
110 - 布尔类型,数据是布尔值
null的前3位也是000,只能通过
Object.prototype.toString.call(null)

知识点2:== 只是比较值,类型自动转化 === 比较值 且比较数据类型

const a = 1;
const b = true;
console.log('==',a == b);  // true
console.log('===',a === b);  // false

1.4 string都被系统包装成对象 - 字符串的操作

  4. 字符串的方法
  
  4.1 字符操作
  const a= '9';
  // 获取Unicode编码
  const unicode = a.charCodeAt(0);
  console.log(unicode);  //结果 57
  // 根据unicode编码,取值
  console.log(String.fromCharCode(unicode));
  
  
  4.2 字符串的提取
  const name = "JavaScript";
  console.log(name.substring(0,4)); // Java
  console.log(name.substr(0,4));   // Java
  console.log(name.slice(0,4));  // Java
  console.log('sdfdsaf345324543'.split('3'));
  
  4.3 字符串的位置索引
  const name = "JavaScript";
  console.log(name.indexOf('Ja'));  // 0
  console.log(name.lastIndexOf('a')); // 3
  
   4.4 字符串大小写切换
  const name = "JavaScript";
  console.log(name.toLocaleUpperCase());
  ​console.log(name.toLocaleLowerCase());
  
   4.5 字符串模式匹配  match  search replace
  const name = "JavaScript";
  const reg = /a/g;
  console.log(name.replace(reg, 'c'));
  
  4.6 字符串其他操作 concat 拼接  trim - 去空格
    console.log('a'.concat('b'));
  console.log('ad  '.trim());

1.5 Bool、Number都被系统包装成对象
64位计算机 Number 类型 - 双精度浮点数 64位

 //  符号位1 + 指数位11 + 52小数位 最大最小是2*1024 2*-1023
 console.log('最大值:',Number.MAX_VALUE);
 console.log('最小值:',Number.MIN_VALUE);
 console.log('非数字:',Number.NaN);
 
 // 5.1 数据转换
 console.log(Number('23'));
 console.log(parseInt('23'));
 console.log(parseInt('23'));
 
 //  5.2 四舍五入
 console.log('向上取整',Math.ceil(9.12)); // 向上取整 10
 console.log('向下取整',Math.floor(9.72));  // 向下取整 9
 console.log('四舍五入',Math.round(9.52));  //四舍五入 10
 console.log('固定精度',9.54634543.toFixed(2));  // 固定精度 9.55
 console.log('固定长度',9.54634543.toPrecision(2));  // 固定长度 9.5
 console.log('取整',parseInt('9.54634543'));  // 取整 9
  1. 引用数据类型 - Object

特点:

  • 实体对象在堆中,指向指针在栈中
  • 内置对象也属于引用类型(Array/Date/Reg/JSON)

说到Object, 就必须聊一聊深拷贝和浅拷贝

 // 1. 浅拷贝 只是拷贝的一个指针指向同一个对象
 const obj = {name:"lzb", age: "23", dog: {'dogName': "塞班"}};
 const obj2 = Object.assign({},obj);
 obj.dog.dogName = "改过的塞班";
 console.log(obj.dog.dogName, obj2.dog.dogName);
 // 结果  改过的塞班 改过的塞班
 
 // // 2. 深拷贝,拷贝对象,修改之后不影响原来对象
 const obj = {name:"lzb", age: "23", dog: {'dogName': "塞班"}};
 const obj2 = JSON.parse(JSON.stringify(obj));
 obj.dog.dogName = "改过的塞班";
 console.log(obj.dog.dogName, obj2.dog.dogName);
 // // 结果  改过的塞班 塞班 

Object常用到的还有一个属性类型

const person = {};
Object.defineProperty(person, 'name', {
    configurable: true, // 能否通过 delete 删除属性从而重新定义属性,或者能否把属性修改为访问器属性。该默认值为 true
    enumerable: true, // 表示能否通过 for-in 循环返回属性。默认值为 true
    // writable: true,  // 能否修改属性的值。默认值为 true
    // value: "lzb",  // 默认值为 undefined
    set: function (newValue) {  // 在写入属性时调用的函数。默认值为 undefined
       this._name = newValue ? `修改的 + ${newValue}`: "不变的值";
    },
    get: function () {   // 在读取属性时调用的函数
        return this._name ? this._name: "lzb";
    }
});

console.log('修改之前的值---',person.name);
person.name = "李四";
console.log('修改之后的值---',person.name);

// 修改之前的值--- lzb
// 修改之后的值--- 修改的 + 李四    

到这里基本了解基础数据类型与引用类型,接下来补充一个常用点,判断数据类型,
方法这么多,我们到底应该使用哪个判断更加准确

// 2.1 typeof  返回字符串 不能判断null、array
console.log(typeof Symbol()); //Symbol
console.log(typeof new Date()); //object
console.log(typeof null);  // object
console.log(typeof [1,23]);  // object

// 2.2 instanceof  A  instanceof B  A是否是B的实例  不能检测null 和 undefined
console.log(([] instanceof Array)); //Array

// 2.3 constructor  -  不能检测null 和 undefined 可能不准确
console.log([1,2,3].constructor === Array);

// 2.4 Object.prototype.toString.call() 最准确
console.log(Object.prototype.toString.call('23'));   // [object String]
console.log(Object.prototype.toString.call(23));    // [object Number]
console.log(Object.prototype.toString.call(false)) ; // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) ; // [object Undefined]
console.log(Object.prototype.toString.call(null)) ; // [object Null]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new Function())); // [object Function]
console.log(Object.prototype.toString.call(new Date())) ; // [object Date]
console.log(Object.prototype.toString.call(new RegExp())) ; // [object RegExp]
console.log(Object.prototype.toString.call(new Error())) ; // [object Error]
console.log(Object.prototype.toString.call(document)) ; // [object HTMLDocument]
console.log(Object.prototype.toString.call(window)) ; //[object global] window是全局对象global的引用

最后得到结果 Object.prototype.toString.call() 最准确

二、变量
  1. 作用域 : 表示某个变量、某个函数在某一个地方(代码块)有效
 ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域   ES6 之后新增块级作用域 新增let const
  var  outScope = "外层数据";
 function outFunc() {
      var inScope = "内层数据";
      function innerFunc() {
          console.log(inScope);
      }
      console.log(outScope);
      innerFunc();
 }
 outFunc();

注意: 所有window对象的属性拥有全局作用域

  1. 块级作用域特点 : 代码执行时遇到花括号,会创建一个块级作用域,花括号结束,销毁块级作用域
  • A.声明变量不会提升到代码块顶
  • B.禁止重复声明
    // 遍历一
    var a = [];
    for (let i = 0; i < 10; i++) {
     a[i] = function () {
         console.log(i);
       }
    }
    a[5]();  // 结果 5 let i 只在for循环内部有效

    //  遍历二
    var a = [];
     for (var i = 0; i < 10; i++) {
     a[i] = function () {
         console.log(i);
        }
    }
    a[5]();  //结果: 10  因为var 是造成变量提升为全局变量 执行完成for 循环之后 var i = 10 所以不管a[x]() 都是 10
  1. 变量 var let const
var  a = 1000;
// A.ES6 之前的使用  1. 允许重复的变量声明:导致数据被覆盖
// B.用var声明的变量会默认被提升到当前作用域的顶部
// C.全局变量会挂载在window


let  b = 2;  // ES6 新增
//  let在全局作用域下定义变量不会挂在到顶层对象window上
//  let声明的变量,不允许当前作用域范围内重复声明
//  let 没有变量提升,所以必须先定义,在使用

const c = 3;   // ES6 新增

//  const 和let特性相同,唯一不同的是,const一般指的都是常量,常量的定义是不可改变的变量,所以用const 申明的变量是不可变的,且只能在定义是进行赋值。


// console.log(window.a); // 1000
// console.log(window.b); // undefined
三. 内置对象
3.1 数组

1.1. 数组中增加元素

const array = new  Array();
array.push("a");  // 往数组末尾添加新元素
array.push('aa','bb');
array.unshift('b');  // 组最前面添加一个新元素
console.log('数组----',array);

1.2. 数组移除元素

array.pop();  // 数组删除末尾元素。
console.log('数组pop----',array);

array.shift(); // 组删除最前面一个元素
console.log('数组shift----',array);

const  newArr = array.slice(0,1);  // 截取指定元素
console.log('数组slice----',newArr);

array.splice(1,1);  // 移除指定元素index ,
console.log('数组splice----',array);

1.3. 数组合并

console.log('合并',[1,2,3].concat(['a','b'])); // 合并之后产生一个新的数组

1.4 数组遍历重要

const  eachList = ['a', 'b', 'c', 'a'];
// forEach  - 遍历 当前元素、当前位置、数组对象
eachList.forEach(function (value, index, array) {
    console.log('forEach - 遍历', value, index, array);
});

// filter 过滤数组 - 所有满足条件的元素 - 可能有多个 - 返回数组
const resultArray  = eachList.filter(function (value, index, array) {
    // console.log('filter - 遍历', value, index, array);
    return value === "a";
});
console.log('filter - ',resultArray);  // filter -  (2) ["a", "a"]

// find 过滤数组 - -第一个满足条件的元素  只有一个元素 - 返回元素
const findResult  = eachList.find(function (value, index, array) {
    // console.log('filter - 遍历', value, index, array);
    return value === "a";
});
console.log('find - ',findResult); // find -  a

// map 遍历数组 - 加工数组,返回加工之后的数组
const mapResult = eachList.map(function (value, index, array) {
    return value  + index;
});
console.log('map - ',mapResult);  // map -  (4) ["a0", "b1", "c2", "a3"]

// reduce  归并操作 对数组每一个元素进行累加,最终返回所有元素之和
const reduceResult = eachList.reduce(function (previousValue, currentValue, currentIndex, array) {
   return previousValue + currentValue;
});
console.log('reduce - ',reduceResult);  // reduce -  abca

// some 对数组中的每一项执行回调函数,如果该函数对任一项返回 true,则停止遍历,并返回 true , 可以用于判断
const someResult = eachList.some(function (value, index, array) {
      return value == "a";
});
console.log('some - ',someResult); // some -  true

// every  对数组中的每一项执行回调函数,如果该函数每一项都返回 true 才是true 所有
const everyResult = eachList.every(function (value, index, array) {
    return value == "a";
});
console.log('every - ',everyResult); //every -  false
2.内置对象 - JSON
  1. json 包括2中对象 json对象 key-value json数组
const person = {name: "lzb", age: 18, score: 100}
console.log('json对象---', person);
const person = [{name: "lzb", age: 18, score: 100}, {name: "ls", age: 18, score: 100}];
  1. json 对象与字符串的转化
const jsonStr = JSON.stringify(person);
console.log('json -> 字符串', typeof jsonStr, jsonStr);
const jsonValue = JSON.parse(jsonStr);
console.log('\n' + '字符串 -> json', typeof jsonValue, jsonValue);
3.内置对象 - Date
  1. 日期常用操作

    const date = new Date();
    console.log('当前的日期',date);
    console.log('一个月中的某一天=',date.getDate());
    console.log('一周中的某一天=',date.getDay());
    console.log('月份 (0 ~ 11)当前的月份=',date.getMonth() + 1);
    console.log('当前的年=',date.getFullYear());
    console.log('日期--->字符串',date.toLocaleString());
    

2.日期格式化

function dateFormat(fmt, date) {
   if(!fmt || !date){
       console.log('格式化不能为null');
       return;
   }
   const opts = {
       "Y+": date.getFullYear().toString(), // 年
       "M+": (date.getMonth()+1).toString(), // 月
       "d+": date.getDate().toString(), // 日
       "H+": date.getHours().toString(), // 时
       "m+": date.getMinutes().toString(), //分
       "s+": date.getSeconds().toString(), //秒
       // 有其他格式化字符需求可以继续添加,必须转化成字符串
   };
   let ret;
   let result = fmt;
   for(let key in opts){
       const regStr = `(${key})`;
       ret = new RegExp(regStr).exec(fmt);
       if(ret){
           const replaceStr =  (ret[1].length == 1) ? (opts[key]) : (opts[key].padStart(ret[1].length, "0"))
           result = result.replace(ret[1],replaceStr);
       }
   }
   return result;
}

console.log('格式化---',dateFormat("YYYY-MM-dd HH:mm:ss", new Date()));   
4.内置对象 - 正则表达式

`正则结构: var expression = / pattern / flags ;

flags取值
g: 表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
i: 表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
m: 表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。`

正在表达式说明

() 的作用是提取匹配的字符串。表达式中有几个()就会得到几个相应的匹配字符串。比如 (\s+) 表示连续空格的字符串。

[] 是定义匹配的字符范围。比如 [a-zA-Z0-9] 表示字符文本要匹配英文字符和数字。

{} 一般用来表示匹配的长度,比如 \d{3} 表示匹配三个数字,\d{1,3} 表示匹配1~3个数字,\d{3,} 表示匹配3个以上数字

^ 匹配一个字符串的开头,比如 (^a) 就是匹配以字母a开头的字符串

$ 匹配一个字符串的结尾,比如 (b$) 就是匹配以字母b结尾的字符串

^ 还有另个一个作用就是取反,比如[^xyz] 表示匹配的字符串不包含xyz 只有出现在[]中才表示取反

\d 匹配一个非负整数, 等价于 [0-9]

\s 匹配一个空白字符

\w 匹配一个英文字母或数字,等价于[0-9a-zA-Z]

.  匹配除换行符以外的任意字符,等价于[^\n]

* 表示匹配前面元素0次或多次,比如 (\s*) 就是匹配0个或多个空格

+ 表示匹配前面元素1次或多次,比如 (\d+) 就是匹配由至少1个整数组成的字符串

? 表示匹配前面元素0次或1次,相当于{0,1} ,比如(\w?) 就是匹配最多由1个字母或数字组成的字符串

4.1.小试牛刀

const  regx1 = /\.doc/gim;
const  regx2 = new RegExp('\\.00-Doc文档','gim');  // 对 \. 再次转义
console.log('regx1',  regx1.test('dsjfjkds.00-Doc文档'));
console.log('regx2', regx2.test('dsjfjkds.00-Doc文档'));    

4.2. 匹配的方法

test、match、replace

console.log('test', /\d+/.test('a123')); // 至少一个数字

console.log('match', '12fgfd345dsfdf'.match(/\d+/g));  // match是获取正则匹配到的结果,以数组的形式返回  ["12", "345"]

console.log('replace','12fgfd345dsfdf'.replace(/\d+/g,'A')); //replace 匹配到了就替换AfgfdAdsfdf    

4.3. 常用示例

// 去除字符串空格
String.prototype.customTrim = function () {
    return this.replace(/(^\s+)|(\s+$)/gim, '');
};

console.log('dsafdss adfdsa fdsaf'.customTrim());

// 获取数据类型
function getDataType(obj){
    let rst = Object.prototype.toString.call(obj);
    rst = rst.replace(/\[object\s(\w+)\]/,'$1'); // [object Xxx]  $1 是正则表达式中第一个() 中匹配的内容。
    return rst.toLowerCase();
}

console.log(this.getDataType(343454));


// 电话号码转化
function telFormat(tel){
    tel = String(tel);
    return tel.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3');
}
console.log(this.telFormat('19876363726'))