ECMAScript

初识JavaScript

1.1.什么是JavaScript ​ JavaScript ( JS ) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。
虽然它是作为开发Web 页面的脚本语言而出名的,但是它也被用到了很多非浏览器环境中,例如
Node.jsApache
CouchDB
Adobe
Acrobat
。JavaScript
是一种基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。

2.JavaScript的组成部分

  1. ECMAScript: 描述了该语言的语法和基本对象。(变量 函数 对象 数组 原型链 原型 设计模式 正则表达式 …)
  2. 文档对象模型(Document Object Model): 操作网页的api和接口。(绑定js事件,获取dom元素)
  3. 浏览器对象模型(Browser Object Model): 操作浏览器的api和接口。(alert ajax 超时调用 间歇调用 location history )
  1. js运行环境:
    1.浏览器 内置js解析器 (解析引擎)
    2.nodejs

4.js特点:

1.解释型语言(需要解析器解析执行)
2.弱类型语言:动态编程语言 可以重复声明(后者覆盖前者),而强类型语言不能重复声明,否则报错。
3.按照代码解析顺序从上到下依次执行
4.区分大小写,大小写敏感(html css大小写不敏感)

js注释:      单行注释 //       多行注释 /**/
html注释:      <!-- -->
css注释:       /
*css */

6.全局作用域和局部作用域 :

函数外部和函数内部
1 使用var声明的变量写在
函数内部就是局部变量 仅当前作用域可见 函数外部就是全局变量 所有作用域可见
2 写在函数内部但是前面不加声明变量关键字就是全局变量

7.在body和head中使用JavaScript的区别 ​ :

  • 在head部分中的JavaScript会在被调用的时候才执行。
  • 在body部分中的JavaScript会在页面加载的时候被执行,
    ​ 因为浏览器解析html是从上到下的。如果把JavaScript放在head里的话,则先被解析,但这时候body还没有解析,所以会返回空值。

一般都会绑定一个监听,当全部的html文档解析完之后再执行代码

windows.onload = function(){
	// 这里放执行的代码
}

8.关键字与保留字

关键字:(在js中有特殊功能)

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

保留字:(将来可能成为关键字)

abstract   enum     int      short
boolean   export   interface    static
byte     extends     long     super
char     final     native      synchronized
class      float      package      hrows
const      goto      private      transient
debugger  double implements  protected
volatile      import     public

浏览器(js前端运行环境)中的js:es+dom+bom

请添加图片描述:
Node.js(js后端运行环境)中的js:

请添加图片描述

ECMAScript(js核心语法)

1.变量

变量声明方式作用域重复声明重新赋值变量提升
var(es5)函数级作用域√(声明变量)
let (es6)块级作用域×√(声明变量)×
const (es6)块级作用域××(声明常量)×
三者区别:
var:
具有函数级作用域(在函数内部可见),不具备块级作用域。
会被提升到包含函数的顶部。
允许重复声明同一变量。
变量可以被重新赋值。

let:
具有块级作用域,只在声明它们的块内可见。
不会被提升到包含块的顶部。
允许重复声明同一变量。
变量可以被重新赋值。

const:
具有块级作用域,只在声明它们的块内可见。
不会被提升到包含块的顶部。
不允许重复声明同一变量。
一旦赋值后,不可被重新赋值,即它们是不可变的(immutable)。




//根据你的需求和最佳实践,你可以选择在代码中使用这些不同的关键字。通常的建议如下:

//如果变量不需要被重新赋值,最好使用 const,因为它提供了更严格的不可变性。

//如果变量需要在同一作用域内重新赋值,但不需要跨作用域可见,使用 let 是合适的。

//尽量避免使用 var,因为它存在变量提升和函数级作用域,可能引发一些不容易察觉的问题。在现代 JavaScript 中,let 和 const 更安全和可预测。

//在 ES6(ECMAScript 2015)之后,let 和 const成为了更现代和推荐的变量声明方式,而 var 的使用已经大大减少。
  • 标识符(指变量,函数,属性的名字或者函数的参数)命名规则 :
    1.由字母数字下划线$组成
    2.不能以数字开头
    3.建议使用驼峰式命名 firstName fontSize
    4.不能使用关键字和保留字
  • 使用var声明变量特点
    1.可以重复声明
    2.使用var声明变量会进行变量提升(将其放到作用域最开始,仅提升变量,不赋值,函数也是)
    3.不存在暂时性死区(在声明变量之前使用变量)
    4.不存在块级作用域 if(){} for(){}

0、var声明变量提升:

console.log(a);//undefind,b、不报错
var a=1;
等价于-->
var a;//仅升变量,不赋值
console.log(a);
  1. 在函数中用var,该变量为局部变量
function foo(){
  if(true){
    var a = 3;  
    console.log("inner",a);//inner 3
  }
  console.log("outer",a);//inner 3  
}
foo();
console.log(a);//error!  
  1. 在函数中不用var,该变量为全局变量:
function test(){
  message = "hello"; 
}
test();
console.log(message); //可以访问
  1. 全局作用域
function b() {
  a = 10;
  return;
}
var a = 1;
b();
console.log(a);//10
x = 1;//添加到全局对象global中 global:{x:1}
console.log(x); //1
function y() {
  console.log(x); //undefined
  console.log(this,'指向全局对象global');
  console.log(this.x);//this函数内部属性  1
  var x = 2;
  console.log(x); //2
}
y();
console.log(x);//1
//函数作用域:局部作用域
var a = 1;
function b() {
  //解析顺序 function a(){} ===>var a;
  a = 10;
  return;
  //a函数声明,提前变量a,将a认为是函数b作用域的变量,具有局部效果
  // 函数也会提升到作用域最前面  写在return后方代码无效
  function a(){}// js具有函数优先(把函数当作变量使用) var a;
} 
b(); 
console.log(a); // 1


/**
 * 解析顺序:
 * function b(){
 *    function a(){} var a;
 *    a = 10;
 *    return 
 * }
 * var a;
 * a = 1;
 * b();
 * console.log(a);
 * 
 */

2.数据类型

JavaScript 拥有动态类型。这意味着相同的变量x可用作不同的数据类型

2.1基本/简单数据类型:Number、String、Boolean、Null、Undefined、Symbol、BigInt

1. Number(数值型)
按照数字精度可以分为整数(int),单精度(float),双精度(double )
按照数字的表示方法可以分为二进制(Binary),八进制(Octal),十进制(decimal system),十六进制(Hexadecimal)

 var a = 10;
 var b = 0x11; //十六进制 0x开头, 取值范围:0-9 a-f
 var c = 011;//八进制 0开头
 var d = 3e5;//科学计数法
 var e = 8/5;
 var f = 5.235;
 console.log(a,b,c,d,e,f);//10 17 9 300000 1.6 5.235
 检测数据类型 typeof 
 console.log(typeof a,typeof b,typeof c,typeof d,typeof e,typeof f);
 //number number number number number number

2. String(字符串型)
使用"" 或者是 ‘’ 包裹就是字符串类型
转义字符 \n 换行 \t 制表 \b 退格 \r 回车 \ 斜杠 ’ 单引号 " 双引号

var a = '10';
var b = "true";
console.log(a,b,typeof a,typeof b);//10 true string string

3. Boolean(布尔型)
只有两个取值:true(值为1)/flase(值为0)

var a = true;
var b = false;
console.log(a,b,typeof a,typeof b);
//true false boolean boolean

4. Null(空引用型)
只有一个取值:null
如果一个变量准备将来保存对象,可以将该变量初始化null而不是其他,这样可以通过检查null值就可以知道相应的变量是否已经保存了一个对象的引用。

if(car !== null ){  //car对象执行某些操作}
var a = null;
console.log(a,typeof a);
//null object

5. Undefined(未定义型)
这个值表示变量不含有值。未定义的。

var a;
console.log(a,typeof a);//undefined 'undefined'
var a = undefined;
console.log(a,typeof a);//undefined 'undefined'

undefined 与null关系 :
undefined继承null,所以undefined ==null结果为true,但是null表示空对象,undefined表示未定义;
null与undefined用途不同,null可以用来表示一个空对象,但是没有必要把一个变量的值显式设置为undefined。

(以上es5,以下es6新增)

6. Symbol (独一无二的值)

var a = Symbol('10');
var b = Symbol('10');
console.log(a==b,typeof a,typeof b);
//false symbol symbol

7. BigInt
当js中值超出最大计数范围 失去精度 可以使用bigInt处理 数字文本后缀加n

console.log(9/0);                                    //Infinity 正无穷
console.log(-9/0);                                   //-Infinity 负无穷
console.log(Number.MAX_VALUE,'js最大值');            //1.7976931348623157e+308  js最大值
console.log(Number.MIN_VALUE,'最小值');              //5e-324 最小值
console.log(Number.MAX_VALUE * 3);                   //Infinity 正无穷大 
console.log(typeof (BigInt(Number.MAX_VALUE) * 3n)); // bigint
2.2引用/复杂数据类型:Object(Arr、Function、Set、Map、Promise)

所有的引用数据类型都是object数据类型的子类型

2.2.1. 对象{Object}:

无序属性的集合。对象是一个包含相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性方法

  1. 创建

1直接字面量 var obj = {name:“zhangsan”}
2构造函数 var obj = new Object();

// 1.字面量
var obj = {
  name:'zhangsan',
  age:12,
  gender:'male',
  // 方法中this指向谁取决于被谁调用
  sayName:function(){
    console.log(this.name)
  },
  // sayName(){
  // },
  // toString(){
  // }
};
// 访问对象属性 点访问法
console.log(obj.name);//zhangsan
console.log(obj.sayName);//[Function: sayName]
obj.sayName();//zhangsan
console.log(obj);//{name: 'zhangsan',age: 12,gender: 'male',sayName: [Function: sayName]}




// 2.使用构造函数创建对象 默认创建空对象
var obj1 = new Object();
// 给空对象添加属性
obj1.name = 'lisi';
obj1.gender ='male';
obj1.sayGender = function(){
  console.log(this.gender)
}
console.log(obj1,'构造函数创建对象');//{ name: 'lisi', gender: 'male', sayGender: [Function (anonymous)] } 构造函数创建对象
obj1.sayGender();//male
  1. 访问

1点访问法 obj.name
2中括号访问法 obj[‘name’] (可解析变量)

var obj = {
  name:'terry',
  color:'white'
}
console.log(obj.name);//terry
console.log(obj['color']);white

// 中括号可以解析变量 
var a = 'age';
obj[a] = 12;//obj['age'] = 12
console.log(obj)
  1. 增删改查对象中的属性
var obj = {
  name:'terry',
  color:'white'
}
// 修改对象中属性 如果属性存在对象修改 不存在就是新增
obj.name = 'larry';
// 新增属性
obj.gender = 'male';
obj['weight'] = '40kg';
console.log(obj);//{ name: 'larry', color: 'white', gender: 'male', weight: '40kg' }

// 删除对象中得属性
delete obj.name;
delete obj['color'];
console.log(obj);//{ gender: 'male', weight: '40kg' }


// 访问对象中得属性 遍历对象 for in循环 for循环升级版
for(var key in obj){
  // key---就是属性名 
  console.log(key,'属性名',obj[key],'属性值')
}//gender 属性名 male 属性值       weight 属性名 40kg 属性值
强制类型转换
var obj = {
  name:'zhangsan',
  age:12,
  valueOf(){
    return this.age
  },
  toString(){
    // return this.name
    return 20
  }
}
// 将对象显示转为布尔值 
console.log(Boolean(obj));   //true
// 将对象显示转为字符串 如果在对象中重写了toString 则会调用toString 
console.log(String(obj),typeof String(obj)); //20 string
// 将对象显示转为Number  默认转为NaN 如果对toString和valueOf重写 则调用重写方法 两者重写调用valueOf
console.log(Number(obj));    //12
console.log(+obj);           //12
console.log(parseInt(obj));  //20  parseInt不调用valueOf 只调用toString
console.log(parseFloat(obj));//20  parseFloat不调用valueOf 只调用toString

检测属性
in自有属性/继承属性:true
.hasOwnProperty()自有属性:true;继承属性:false
.propertyIsEnumerable()hasOwnProperty()的增强版。自身属性&&枚举属性(自己创建的属性):true
var obj = {
  name:"zhangsan",
  age:12
}
//in
console.log('name' in obj);       //true
console.log('age' in obj);        //true
console.log('gender' in obj);     //false
console.log('toString' in obj);   //true   因为toString定义在object对象中,而所有对象最终都会在原型链上指向object,所以obj也拥有toString属性。


//  hasOwnProperty 检测属性是否是对象自有属性 对于继承属性返回false
console.log(obj.hasOwnProperty('name'));     //true
console.log(obj.hasOwnProperty('toString')); //false,toString为继承属性
console.log(obj.hasOwnProperty('valueOf'));  //false

// propertyIsEnumerable 检测是否是自有属性 可枚举属性 返回true 
console.log(obj.propertyIsEnumerable('name'));      //true
console.log(obj.propertyIsEnumerable('age'));       //true
console.log(obj.propertyIsEnumerable('toString'));  //false,不可枚举
console.log(obj.propertyIsEnumerable('valueOf'));   //false
console.log(obj.propertyIsEnumerable('gender'));    //false
原型属性及方法(原型方法,实例可以调用的方法)
  • 在Object的构造函数的原型对象中的属性和方法都可以被Object构造函数的实例继承

  • Object原型中的所具有的任何属性和方法也同样存在于其他对象中,任何对象继承自Object。
    在这里插入图片描述
    总结:每个构造函数都有一个原型对象;原型对象都包含一个指向构造函数的指针;实例都包含一个指向原型对象的内部指针。 ----《JavaScript高级程序设计》

构造函数本身调用的方法静态方法 :Object.defineProperty()…
实例对象调用的方法是实例方法也是原型方法 : toString() valueOf() hasOwnProperty()…

/**
 * 原型:每一个构造函数都有一个指针(prototype)指向原型对象,
 * 每一个原型对象都有一个指针(constructor)指向构造者,构造函数创建实例都有一个指针(__proto__)
 * 指向原型对象。
 */
var obj = new Object(); //构造函数是Object,实例是{}里的
var obj1 = {};//也是构造函数创建

// 构造函数
console.log(Object);                                    //[Function: Object]

// 该构造函数所对应的原型对象
console.log(Object.prototype);                          //[Object: null prototype] {}

// 每一个原型对象都有指针指向构造者
console.log(Object.prototype.constructor === Object);                   //true

// 每一个实例对象都有一个指针指向原型对象
console.log(obj.__proto__ === Object.prototype);                        //true
console.log(obj.__proto__.toString() === Object.prototype.toString());  //true
console.log(obj.__proto__.valueOf() === Object.prototype.valueOf());    //true
// 对象实例可以访问的方法和属性 实例方法也叫原型方法 

  •   Object原型中常用的方法:
    
  • .prototype.constructor
定义属性
  1. 数据属性:

功能: 配置当前属性是否可删除 是否可修改 是否可枚举

Object.defineProperty(目标对象,属性,{属性特性}) 或 Object.defineProperty(目标对象,{属性1:{属性特性1},…,{属性n:{属性特性n})

 属性四大特性:
configurable:false,//是否删除,默认true
writable:true,     //是否更改,默认true
enumerable:true,   //是否可枚举 默认值true 可以使用for in循环遍历显示出来,若改成false,则不能通过for in遍历出来,就看不见了
value:'terry',     //给属性设置属性值 
var obj = {
  name:'zhangsan',
  age:12,
  gender:'male'
};
// obj.name = 'lisi';
// delete obj.age;
// for(var key in obj){
//   console.log(key)
// }
/**
 * 修改属性默认特性:构造函数本身的方法 静态方法只能由构造函数本身调用
 * Object.defineProperty(目标对象,属性,{配置对象})
 */
Object.defineProperty(obj,'name',{
  configurable:false,//默认对象属性是可以删除的 默认值true  改为false后该属性无法删除
  writable:true,     //表示当前属性是否可写 默认值true 
  enumerable:true,   //表示当前属性是否可枚举 默认值true 可枚举属性可以使用for in循环遍历
  value:'terry',     //给name属性设置属性值
});
delete obj.name;
// obj.name = 'lisi';
for(var key in obj){
  console.log(key)          //name age gender
}
console.log(obj.propertyIsEnumerable('name'));//true,检测属性是否可枚举

// 修改多个属性默认特性 defineProperties(obj,{})
Object.defineProperties(obj,{
  name:{
    // 每一个属性都可以设置可修改可删除可枚举
    writable:false
  },
  age:{
    configurable:false
  },
  gender:{
    enumerable:false
  }
});
obj.name = 'larry';
delete obj.age;
for(var key in obj){
  console.log(key)//name age
}
console.log(obj)//{ name: 'terry', age: 12 }
  1. 读取属性

功能: 返回所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

Object.getOwnPropertyDescriptor(目标对象,属性名));
读取整个对象中属性特性 :getOwnPropertyDescriptors(obj)

var obj = {
  name:'zhangsan',
  age:12,
  gender:'male'
}
/**
 * 读取对象中一个属性特性 getOwnPropertyDescriptor(obj,'name')
 */
Object.defineProperties(obj,{
  name:{
    writable:false
  },
  age:{
    configurable:false
  },
  gender:{
    enumerable:false
  }
});
console.log(Object.getOwnPropertyDescriptor(obj,'name'));   //{ value: 'zhangsan', writable: false, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptor(obj,'gender')); //{ value: male, writable: true, enumerable: false, configurable: true }
console.log(Object.getOwnPropertyDescriptor(obj,'age'));    //{ value: 12, writable: true, enumerable: true, configurable: false }


/**
 * 读取整个对象中属性特性 getOwnPropertyDescriptors(obj)
 */
console.log(Object.getOwnPropertyDescriptors(obj));
// {name: {value: 'zhangsan',writable: false,enumerable: true,configurable: true },
//   age: { value: 12, writable: true, enumerable: true, configurable: false },
//   gender: {value: 'male',writable: true,enumerable: false,configurable: true }
// }
  1. 访问器属性
    访问器属性不包含数值,它包含的是一对getter和setter函数;
    访问器属性不能像数据属性一样直接定义,它必须使用Object.defineProperty()方法来定义
属性特性:
configurable:false,//默认对象属性是可以删除的 默认值true  改为false后该属性无法删除
enumerable:true,   //表示当前属性是否可枚举 默认值true 可枚举属性可以使用for in循环遍历
getfunction(){}	//在读取属性时调用的函数,默认值为undefined
set: function(){}	//在写入属性时调用的函数,默认值为undefined

var obj = {
  // 一般对象中私有属性 _year 私有属性 不希望被直接访问
  _year:2023
};
// 访问器属性可以控制私有属性可读 可写 
Object.defineProperty(obj,'year',{
  // 访问器属性 
  configurable:true,
  enumerable:true,
  // 读取的时候触发
  get:function(){
    console.log('我被读取了')       //我被读取了
    return this._year
  },
  // 修改的时候触发 newYear接收到的是修改的值
  set:function(newYear){
    if(this._year != newYear){
      this._year = newYear
    }
  }
});
console.log(obj.year);            //2023
obj.year = 2024;
console.log(obj);             //{ _year: 2024, year: [Getter/Setter] }


对象序列化

对象序列化是指将对象的状态转换为字符串,也可以反序列化,将字符串还原为对象函数。

  • RegExp,Error对象,undefined值不能序列化和反序列化。

JSON.stringify(obj) 将对象序列化为JSON字符串,只能序列化对象可枚举的自有属性

JSON.parse(jsonStr) 反序列化
在这里插入图片描述

// 对象序列化 将对象转为json字符串 
var obj = {
  name:'zhangsan',
  age:12
};
var jsonStr = JSON.stringify(obj);
console.log(jsonStr,typeof jsonStr);  //{"name":"zhangsan","age":12} string

// 对象反序列化 将json字符串转为js对象
var objParse = JSON.parse(jsonStr);
console.log(objParse);                //{ name: 'zhangsan', age: 12 }
console.log(obj == objParse);         //false,两者虽然内容一样,但引入地址不同

obj.name = 'lisi';
console.log(obj,objParse);            //{ name: 'lisi', age: 12 } { name: 'zhangsan', age: 12 }
// ******************** 实现深拷贝方法 
// 1.JSON.parse(JSON.stringify())
2.2.1.1 数组[Array]

有序列表集合

特点:
1.可以存放任意数据类型
2.大小可以动态调整

1. 创建

  • 字面量 var arr = [1,2,3,4,5];
  • Array构造函数
// 1.字面量 
var arr = [1,'hello',null,undefined,{name:'zhangsan'},function(){},[1,2,3,4]];
console.log(arr,arr.length,'数组长度等于数组元素个数');//[1,'hello',null,undefined,{name:'zhangsan'},[function(anonymous)],[1,2,3,4]]      7      数组长度等于数组元素个数
arr.length = 4;//增加或者减少数组长度
console.log(arr);			//[ 1, 'hello', null, undefined ]

// 2.构造函数创建数组实例
var arr = new Array('10',true,null,{name:'zhangsan'});//设置一个number整数将作为数组长度 小数报错
console.log(arr,arr.length);//[ '10', true, null, { name: 'zhangsan' } ] 4
console.log(Array.prototype);//Object(0) []
console.log(Array.prototype.constructor);//[Function: Array]
console.log(Array.prototype.constructor === Array);//true
console.log(arr.toString());//10,true,,[object Object]
console.log(arr.valueOf());//[ '10', true, null, { name: 'zhangsan' } ]

2. 访问
数组变量名[索引]

var arr = ["one","two","three"];
arr[0];//one
arr[10];//undefined
arr[3]="four";//添加元素,数组长度变为4




// 遍历数组元素 for in 循环
var arr = new Array('10',true,null,{name:'zhangsan'});
for(var key in arr){
  console.log(key,arr[key])
}
//0 10
//1 true
//2 null
//3 { name: 'zhangsan' }
for(var i = 0;i<arr.length;i++){
  console.log(i,arr[i])
}
//0 10
//1 true
//2 null
//3 { name: 'zhangsan' }

4. 方法

静态方法(构造函数调用)

  • JSON.stringify(arr)                将数组序列化为JSON字符串(只能序列化对象可枚举的自有属性)
  • JSON.parse(jsonArr)               反序列化(JSON字符串转换为数组)
  • Array.isArray(变量)                判断是不是数组 ,数组返回true 非数组返回false
  • Array.from(变量)                 将类数组对象/字符串转为数组
  • Array.of(数组元素)                 创建一个数组实例(ES6 新增)
  • Math.max(变量)                     返回数组最大值

实列方法(自身提供)/原型方法(实例调用)

  • .tostring()                                转换为字符串,逗号为间隔符
  • .join(“间隔符”)                         转换为字符串,可调参数来自定义间隔符
  • .pop()                                       删除最后一个数组元素
  • .push(添加的元素)                   添加元素到最后
  • .shift()                                       删除首个元素
  • .unshift(添加的元素)                在开头添加新元素
  • .sort( [function(a,b){}] )            字母顺序排序
  • .reverse()                                 反转数组元素
  • .splice(index ,num ,item1,item2)             改变(增删改)数组内容

以上都会修改原数组

  • .concat(被拼接数组)                                    连接数组
  • .slice(index1,index2)                             截取
  • .indexOf(查找元素,何处index查)            从前往后查找想要的数组元素,然后返回index
  • .lastIndexOf(查找元素,何处index查)      从后往前查找想要的数组元素,然后返回index
  • .forEach( 函数 )                                             循环遍历数组元素
  • .every( 函数 )                                    判断每一个数组元素是否符合表达式 。全部符合返回true ;只要有一项不符合直接返回false 跳出循环
  • .some(函数)                                        判断数组元素是否符合表达式。 一项符合就返回true 跳出循环 ;全部不符合才返回false
  • .filter(函数)                                                    过滤符合条件的数组元素组成新数组
  • .map(函数)                                                     返回新数组,新数组为原数组值调函数后的
// 将数组转为字符串方法 
var arr = [1,2,3,4,{name:'zhangsan'}];
console.log(arr.toString(),typeof (arr.toString()));    //1,2,3,4,[object Object] string
console.log(arr.join(' '),typeof (arr.join()));         //1 2 3 4 [object Object] string
//JSON.stringify(arr)     			JSON.parse(jsonArr)
var res = JSON.stringify(arr);
console.log(res,typeof res);            //[1,2,3,4,{"name":"zhangsan"}] string
console.log(JSON.parse(res));           //[ 1, 2, 3, 4, { name: 'zhangsan' } ]



//Array.isArray(变量) 				判断是不是数组 ,数组返回true  非数组返回false
var obj = {name:'zhangsan'};
var a = 'hello';
var b = [1,2,3,4];
var str = 'hello';
console.log(Array.isArray(obj));        //false
console.log(Array.isArray(a));          //false
console.log(Array.isArray(b));          //true
console.log(Array.isArray(str));        //false



//Array.from()				 将类数组对象/字符串转为数组
var str = 'hello';//字符串可以使用for 循环 for in循环遍历
for(var i=0;i<str.length;i++){
    console.log(i,str[i])                    //0 h  1 e  2 l  3 l  4 o
}
for(var index in str){
    console.log(index,str[index]);          //0 h  1 e  2 l  3 l  4 o
}
var res = Array.from(str);
console.log(res,Array.isArray(res));        //[ 'h', 'e', 'l', 'l', 'o' ] true



//Array.of() ES6 新增 						创建一个数组实例
var arr = Array.of(10);//number整数 表示数组元素,而不是创建10个数组元素
var arr = Array.of(10,'hello',true,null,{},[],function(){});//number整数 表示数组元素
console.log(arr,arr.length);				//[ 10, 'hello', true, null, {}, [], [Function (anonymous)] ] 7
var arr1 = new Array();
console.log(arr1.__proto__.constructor === arr.__proto__.constructor,arr.constructor);		//true [Function: Array]



//Math.max()            返回数组最大值
var arr0=[0,1,2];
var arr1=Math.max(...arr0);
console.log(arr1);              //2







以下是实例方法

//.tostring()          			转换为字符串,逗号为间隔符
var arr0=["one","two","three","four"];
var arr1=arr0.toString();
console.log(arr1);           //one,two,three,four



//.join()       			   转换为字符串,可调参数来自定义间隔符
var arr0=["one","two","three","four"];
var arr2=arr0.join("+");
console.log(arr2);           //one+two+three+four



//.pop()         				  删除最后一个数组元素
//参数:无  		返回值:被删除最后一项数组元素		 修改原数组 
var arr0=["one","two","three","four"];
var arr3=arr0.pop();
console.log(arr3);           //four
console.log(arr0);           //[ 'one', 'two', 'three' ]



//.push(添加的元素)        	 		 添加参数中的值到最后
//参数:要添加的数组元素        返回值:新数组长度          修改原数组
var arr0=["one","two","three","four"];
var arr4=arr0.push();
console.log(arr4);           //4
console.log(arr0);           //[ 'one', 'two', 'three', 'four' ]
var arr5=arr0.push("five");
console.log(arr5);           //5
console.log(arr0);           //[ 'one', 'two', 'three', 'four', 'five' ]



//.shift()      	 	   				 删除首个元素
//参数:无      返回值:被删除得第一个数组元素       修改原数组
var arr0=["one","two","three","four"];
var arr1=arr0.shift();
console.log(arr1);           //one
console.log(arr0);           //[ 'two', 'three', 'four' ]



//.unshift(添加的元素)         			   在开头添加新元素 
//参数:要添加的数组元素         返回值:返回新数组长度      修改原数组
var arr0=["one","two","three","four"];
var arr1=arr0.unshift();
console.log(arr1);           //4
console.log(arr0);           //[ 'one', 'two', 'three', 'four' ]
var arr2=arr0.unshift("zero");
console.log(arr2);           //5
console.log(arr0);           //[ 'zero', 'one', 'two', 'three', 'four' ]



//.sort(  [function(a,b){}]  )      		   字母顺序排序
//参数:无            返回值:根据字符编码排序后的数组        修改原数组
//  参数:function(a,b){}     返回值:排序好数组           修改原数组
var arr = [1,11,3,22,2,33];
var res = arr.sort();
console.log(res);			//[ 1, 11, 2, 22, 3, 33 ] 
var res = arr.sort(function(a,b){
  return a-b//a-b为升序;;b-a为降序;
});
console.log(res);			//[ 1, 2, 3, 11, 22, 33 ]



//.reverse()       	  				  反转数组元素
//参数:无           返回值:返回反转后的新数组    修改原数组
var arr0=["one","two","three","four"];
var arr1=arr0.reverse();
console.log(arr1);              //[ 'four', 'three', 'two', 'one' ]



//.splice(index ,num ,item1,item2)            改变(增删改)数组内容
//参数:index num item、item1    返回值:被删除数组元素组成新数组      修改原数组
var arr = [1,2,3,4,5];
// 删除数组元素 
var res = arr.splice(1);					//从索引为1位置删除到末尾结束 
var res = arr.splice(2,2);					//从索引为2位置删除2项数组元素
var res = arr.splice(2,1,'hello','tom');	//从索引为2位置开始删除1项,同时增加一些项
var res = arr.splice(2,0,'hello','tom');	//从索引为2位置开始增加一些项
var res = arr.splice(-2,2);					//从倒数第几项开始删除数组元素



//.concat(被拼接数组)      	      连接数组
//参数:被拼接的数组         返回值:返回拼接好的数组副本          不修改原数组
var arr0=["one","two","three","four"];
var arr00=["1","2","3","4"]
var arr000=["q","w","e","r"]
var arr1=arr0.concat(arr00,arr000);
console.log(arr1);          //['one','two','three','four','1','2','3','4','q','w','e','r']


//.slice(index1,index2)      		   截取index1~index2的元素,不包括index2
//参数:startindex endindexe            返回值:返回截取后的数组元素组成新数组         不修改原数组   
var arr = ['hello',true,null,undefined,{name:"zhangsan"}];
// 正数 start 从start位置截取到数组元素末尾 
var res = arr.slice(2);				//[ null, undefined, { name: 'zhangsan' } ]
// 正数  从下标start开始截取下标end结束 
var res = arr.slice(2,4);			//[ null, undefined ]
// 负数  从倒数第几项截取倒数第几inddex 
var res = arr.slice(-2,-1);			//[ undefined ]
var res = arr.slice(-4,3);			//[ true, null ]
var res = arr.slice(2,-1);			//[ null, undefined ]



 //.indexOf(查找元素,何处index查) 			从前往后查找想要的数组元素,然后返回index
// 参数:查找元素 从何处开始查可选(index)             返回值:返回该元素的索引或者-1(找不到)              不修改原数组
var arr = [1,2,3,4,5,3,2,1];
var res = arr.indexOf(3,3);
console.log(res);				//	5	



//.lastIndexOf(查找元素,何处index查) 		从后往前查找想要的数组元素,然后返回index
//参数:查找数组元素  index:从何处开始从后往前查               返回值:返回元素索引或者-1(找不到)              不修改原数组
var arr = [1,2,3,4,5,3,2,1,4];
var res = arr.lastIndexOf(2,4);
console.log(res);				// 1



//.forEach(  函数  )   		 for循环升级版 循环遍历数组元素
//参数:function(item,index,arr){}         返回值:就算设置了也没有返回值          不修改原数组 
//item--->数组每一项;;  	index--->数组每一项所对应的下标;;	   arr--->数组本身
var arr = ['tom','jack','larry','terry'];
var res = arr.forEach(function(item,index,arr){
  console.log(item,index,arr);		//tom 0 [ 'tom', 'jack', 'larry', 'terry' ]      jack 1 [ 'tom', 'jack', 'larry', 'terry' ]        larry 2 [ 'tom', 'jack', 'larry', 'terry' ]       terry 3 [ 'tom', 'jack', 'larry', 'terry' ]
  return 1
});
console.log(res);				//undefined



//.every(  函数  )				判断每一个数组元素是否符合表达式 。全部符合返回true  ;只要有一项不符合直接返回false 跳出循环
//参数:function(item,index,arr){}             返回值:true或者false           不修改原数组
var arr = [1,2,3,4,5];
var res = arr.every(function(item,index,arr){
  console.log('every')
  return item>0
});
console.log(res);//every every every every every true



//.some(函数) 				判断数组元素是否符合表达式。 一项符合就返回true 跳出循环 ;全部不符合才返回false
//参数:function(item,index,arr){} 			返回值:true或者false 			  不修改原数组
var arr = [1,2,3,4,5];
var res = arr.some(function(item,index,arr){
  console.log('some')
  return item>5
});
console.log(res)//some  some  some  some  some   false



//.filter(函数)			过滤符合条件的数组元素组成新数组 
// 参数:function(item,index,arr){} 			 返回值:返回符合条件数组元素组成新数组 		 不修改原数组
var arr = [1,2,3,4,5];
var res = arr.filter(function(item,index,arr){
  return item>2
});
console.log(res);			//[ 3, 4, 5 ] 



//.map(函数)				 返回新数组,新数组为原数组值调函数后的。映射 对每一个数组元素都进行操作
//参数:function(){ }      返回值:新数组,新数组值为原数组值调函数后          不修改原数组
var arr = [1,2,3,4,5];
var data = [  {id:1,name:'zhangsan'},  {id:2,name:'lisi'}, {id:3, name:'wangwu'}];
var res1 = data.map(function(item){
  return item.name
});
console.log(res1);			//[ 'zhangsan', 'lisi', 'wangwu' ]
var res = arr.map(function(item,index,arr){
  return item+5;
});
console.log(res);			//[ 6, 7, 8, 9, 10 ] 
 
var arr0 = [3, 9];
var arr1 = arr0.map((x,i)=>({key: 'key'+i, value: x}));
console.log(arr1);   //返回 [{key:"key0",value:3},{key:"key1",value:9}]

以上方法的重构
重构方法:Array.prototype.方法名 = function () {}

//重构pop方法 要求重写myPop实现pop一样得功能
Array.prototype.myPop = function () {
  if (this.length > 0) {
    var last = this[this.length - 1];
    this.length--;
    return last;
  }else{
    return undefined
  }
};
var arr = [];
var res = arr.myPop();//myPop方法被arr调用 方法内部this指向arr
console.log(res, arr);




/**
 * 重构push方法
 */
Array.prototype.myPush = function(){
  // arr[arr.length] 将新增数组元素添加给原数组元素最后一项下一项
  // 循环遍历实际参数 'tom' 'jack' 'zack'
  for(var i=0;i<arguments.length;i++){
    // 将每一个实际参数依次从末尾添加给原数组 
    this[this.length] = arguments[i]
  }
  return this.length
} 
var arr = [1,2,3,4];
var res = arr.myPush('tom','jack','zack','world');
console.log(res,arr)





/**
 * 重构shift方法 
 */
Array.prototype.myShift = function(){
  // 返回第一项 
  var first = this[0];
  for(var i=0;i<this.length;i++){
    // 将后面的数组元素往前移动一项
    this[i] = this[i+1]
  }
  this.length--;
  return first
}
var arr = [1,2,3,4,5];
var res = arr.myShift();
console.log(res,arr);





/**
 * 重构unshift 
 */
Array.prototype.myUnshift = function(){
  // 获取新数组长度 
  var length = this.length + arguments.length;
  // [1,2,3,4,5]//4     i=4
  // [,,,1,2,3,4,5]//7  i=4+arguments.length  i 7   i = i-arguments.length
  // ['tom','larry','hello',1,2,3,4,5]
  // 将原数组元素向后移动arguments.length位 
  for(var i = length-1;i>=0;i--){
    // 当i等于arguments.length-1 原数组元素移动完成
    if(i>arguments.length-1){
      this[i] = this[i-arguments.length]
    }else{
      this[i] = arguments[i]
    }
  }
  // 新数组下标i为auguments.length-1 
  // 返回新数组长度
  return length
}
var arr = [1,2,3,4,5];
var res = arr.myUnshift('tom','larry','hello');
console.log(res,arr);




/**
 * 迭代方法 forEach
 */
var arr = [1, 2, 3, 4, 5];
Array.prototype.myForEach = function (callback) {
    // callback--->function(){}
    for (var i = 0; i < arr.length; i++) {
        callback(arr[i], i, arr)
    }
};
arr.myForEach(function (item, index, arr) {
    console.log(item, index, arr)
});




/**
 * every 判断数组元素是否符合表达式 只要有一项不符合直接返回false 跳出循环
 */

Array.prototype.myEvery = function (callback) {
    for (var i = 0; i < arr.length; i++) {
        // 把函数调用当作条件的判断
        if (!callback(arr[i], i, arr)) {
            return false
        }
    }
    return true
}
var arr = [1, 2, 3, 4, 5];
var res = arr.myEvery(function (item, index, arr) {
    return item > 1
});
console.log(res, arr);




/**
 * some 判断数组元素是否符合表达式  只要有一个符合就返回true 跳出循环
 */
Array.prototype.mySome = function (callback) {
    for (var i = 0; i < arr.length; i++) {
        // 只要有一个满足条件 直接返回true
        if (callback(arr[i], i, arr)) {
            return true
        }
    }
    return false
}
var arr = [1, 2, 3, 4, 5];
var res = arr.mySome(function (item, index, arr) {
    return item > 5
});
console.log(arr, res);




/**
 * filter 过滤符合条件数组元素组成新数组
 */
Array.prototype.myFilter = function (callback) {
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        if (callback(arr[i], i, arr)) {
            // newArr.push(arr[i])
            newArr[newArr.length] = arr[i]
        }
    }
    return newArr
}
var arr = [1, 2, 3, 4, 5];
var res = arr.myFilter(function (item, index, arr) {
    return item > 4
});
console.log(res, arr);






/**
 * map 映射 对每一个数组元素进行操作
 */
Array.prototype.myMap = function (callback) {
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        newArr[newArr.length] = callback(arr[i], i, arr)
    }
    return newArr
};
var arr = [1, 2, 3, 4, 5];
var res = arr.myMap(function (item, index, arr) {
    return item + 5
});
console.log(res, arr)
2.2.1.2 函数 Function(){}

功能:代码复用
构造函数名必须大写

  1. 声明
function   函数名  (形式参数) {   
         函数体
         return result;	//返回值,返回执行的结果给被调用的
         console.log(1);	//return后面语句不执行
} 
函数名(实际参数);			 //函数声明提升到代码的最前边,可以直接调用函数,调了才能使用函数
内部属性:arguments,this

只有在函数内部才能访问的属性。this也可以在函数外部进行使用。

  • arguments:保存实际参数
// arguments:可以不使用形参,通过函数内部使用arguments保存实际参数
function foo(){
  console.log(arguments,arguments.length,arguments[0],arguments[4]);		//{ '0': 1, '1': 2, '2': 3, '3': 4 } 4 1 undefined
  // for(var i=0;i<arguments.length;i++){
  //   console.log(i,arguments[i]);
  // }
  // for(var key in arguments){
  //   console.log(key,arguments[key]);
  // }
}
foo(1,2,3,4);




//callee 属性是arguments内部成员 仅当当前函数正在执行时候才可用,指向当前执行函数
function fc(n){
  console.log(arguments.callee === fc);//true
  if(n==1){
    return 1
  }
  // return n * fc(n-1)
  return n * arguments.callee(n-1)
}
console.log(fc(6));
/**
 * 斐波那契数列 前两项之和相加等于 第三项
 * 1 1 2 3 5 8 13 21 34 55 89
 * f(0) 1 
 * f(1) 1 
 * f(2) 2 = f(1) + f(0)
 * f(3) 3 = f(2) + f(1)
 * f(10) = f(9) + f(8)
 */
function fb(n){
  if(n<=1){
    return 1
  }
  return arguments.callee(n-1) + arguments.callee(n-2)
}
console.log(fb(10));
  • this
    直接调用this,this指向谁
    函数在函数内调用----->全局
    函数在方法内调用----->调用者
  • 1.单独使用this 在nodejs中 ---->{} ;; 在浏览器中 ---->window全局对象
  • 2.函数内部使用this ---->全局对象
    nodejs global全局对象 浏览器环境 window全局对象
  • 3.在方法内部使用this ---->调用者
  • 4.在事件中使用this ---->接收事件的元素
  • 5.修改this指向:
    5.1.call(执行环境对象,实际参数列表)
    5.2.apply(执行环境对象,实际参数数组列表---->[实际参数])
    5.3.bind(执行环境对象)—>返回的是函数本身
       bind(执行环境对象,实际参数列表)(实际参数列表)
console.log(this);      //{}
function foo(){
  console.log(this)
}
foo();                  //全局


var name = 'larry';
var obj = {
  name:'terry',
  age:10,
  sayName:function(){
    console.log(this);   		 //全局
    console.log(this.name)//terry
    console.log(obj.name)		//zhangsan
  }
};
// obj.sayName();
var x = obj.sayName;
x();//全局对象调用方法 this指向全局对象  










/**
 * 如何修改this指向问题:
 *  
 */
var obj = {
  name:'zhangsan',
  sayName:function(a,b){
    console.log(this.name,a,b,arguments)  
  }
};
var obj1 = {
  name:"lisi",
  sayName:function(){
    console.log(this.name)  
  }
};
obj.sayName();                             //zhangsan undefined undefined [Arguments] {}
// 修改this指向 call(执行环境对象,实际参数列表)
obj.sayName.call(obj1,'hello','tom');    //lisi hello tom [Arguments] { '0': 'hello', '1': 'tom' }
// 修改this指向 apply(执行环境对象,[实际参数列表])
obj.sayName.apply(obj1,[1])             //lisi 1 undefined [Arguments] { '0': 1 }
// 修改this指向 bind(执行环境对象); bind() 返回的是函数本身  bind(执行环境对象,实际参数列表)(实际参数列表)
var res = obj.sayName.bind(obj1);
console.log(res);                     //[Function: bound sayName]
obj.sayName.bind(obj1,1)(2);          //lisi 1 2 [Arguments] { '0': 1, '1': 2 }

作用域
  • 使用var声明变量就是局部变量 函数外部无法访问
  • var声明变量没有块级作用域,是函数级作用域。 (作用在函数内,不被if(){} for()束缚)但const,let就有块级作用域
  • 全局作用域:声明在函数外部的变量,就是全局变量,函数内部和函数外部都可以访问
  • 作用域可以隔离变量 同名变量也不会冲突

作用域链:当前作用域没有声明直接使用变量,该变量就成为自由变量,自由变量会沿着作用域一层一层向上寻找

var v1 = 10,
v2 = 20; 
function foo() {
  var a = 3;
  console.log(v1, v2);  //10 20
  console.log(this);    //全局
}
foo();
// console.log(a);      /报错,a没定义


var a = 10;
function foo() {
  console.log(a);   //undefined
  var a = 100;      //let 报错 因为let声明变量不会变量提升 
  console.log(a);   //100
  function fn() {
    console.log(a); //undefined
    var a = 200;  
    console.log(a); //200
  }
  fn()
}
foo()




//作用域链
var a = 100
function F1() {
  var b = 200
  function F2() {
    var c = 300
    console.log(a);//100
    console.log(b);//200
    console.log(c);//300
  }
  F2()
}
F1()
立即执行函数 IIFE

(Immediately Invoked Function Expressio)意为立即调用的函数表达式,也就是说,函数在声明的同时就会执行。
IIFE形式的函数调用: ( function 名(){} )()

作用:
1.页面加载时候函数只调用一次
2.函数内部变量仅局部可见,不会污染全局变量

var res = (function(){
  var a =10,b=20;
  console.log(a+b);//30
  return a + b
})();
console.log(res);//30

(function(x,y){
  console.log('我是立即执行函数2',x,y,arguments,this)
}(6,8));//我是...  6  8  [Arguments] { '0': 6, '1': 8 } 全局

var i=0;
for (; i < 6; i++) {
  function output() {
    console.log(i); 
  }
}
output()//6

for(var i=0;i<6;i++){
  (function(j){
    console.log(j)
  }(i))
}//0 1 2 3 4 5 
函数调用

调用方法:

  • 函数名(实参列表);

  • 函数名.call(执行环境对象,实参列表);

  • 函数名.apply(执行环境对象,实参列表数组);

  • 函数名.bind(执行环境对象,实参列表)(实参列表); //实参可以写在任意一个小括号中

总结:
call和apply都是改变上下文中的this并立即执行这个函+++++数,
bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加

var obj = {
  name:'zhangsan',
  sayName:function(){
    console.log(this.name)
  }
};
obj.sayName();            //zhangsan
obj.sayName.call(obj);    //zhangsan
obj.sayName.apply(obj);   //zhangsan 第二个参数必须为数组
obj.sayName.bind(obj)();  //zhangsan 加上第二个()才会调用






//bind()()
var obj = {
  name: 'briup',
  sayName: function (a,b,c) {
    console.log(this.name);
    console.log(a,b,c); // 1,2,3
  }
}
var b = obj.sayName;
b.bind(obj); // 代码没有被打印,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。
// 新建一个变量c来接收bind修改后的函数
var c = b.bind(obj, 1, 2);
console.log(c); // 发现c是一个[Function: bound sayName]函数
// 执行c
c();//briup
c(3); // briup
函数应用
  • 作为参数 回调函数
  • 作为返回值
//回调函数:将函数当作参数进行传递 ( 主函数的事先做完,回头再调用传进来的那个函数。)
function A(callback){
  console.log('我是主函数');
  // 模拟回调函数  主函数先执行 回头调用回调函数
  setTimeout(function(){
    callback()
  },1000)
}
function B(){
  console.log('我是回调函数');
}
A(B);           //我是主函数     我是回调函数



/**
 * 将函数作为返回值 
 */
var a = 10
function fn() {
  var b = 20;
  return function(){
    console.log(a+b)
  }
}
var x = fn(), 
b = 200;
x();



// 调用一次函数 数组依次减少 闭包 把函数当作返回值
function sum(){
  var a = 10;
  return function(){
    a--;
    console.log(a)
  }
}
var res = sum();
res();//9
res();//8
res();//7
res();//6
闭包函数

可以访问另一个作用域的变量的函数。
闭包的函数会保存在内存中

三个条件:
1.函数内部嵌套函数
2.内部作用域存在对外部作用域变量的引用
3.变量不会被回收和销毁

  • 优点:
    缓存变量,不会污染全局变量
  • 缺点:
    造成内存泄漏,造成性能问题,变量不会被回收的
    谨慎使用闭包
function f1() {
  var n = 999;
  nAdd = function () { n += 1 }
  function f2() {
    console.log(n);
  }
  return f2;
}
var result = f1();
result();//999
nAdd();//1000
result();//1000   因为nAdd之前调用过,转为闭包其值不会回收,所以n=1000
2.3基本数据类型和引用数据类型在内存中如何存储
  • 基本数据类型

变量和值都是存在栈区的,互不影响的

var a=123;b=a;若再让a=456,会把a的值改为456,但对b没有影响

请添加图片描述
结论:基本数据类型的值存在栈,值与值之间独立存在,修改一个值不会影响其他变量

  • 引用数据类型

变量和引用地址是存在栈区的,内容存在堆区

请添加图片描述
结论:当栈存放引用类型时,值为对象的地址,obj与obj1指向同一个地址,所以当obj的name值变为“lisi”时,obj1也会发生变化

引用数据类型赋值拷贝的是引用地址,基本数据类型赋值拷贝的是值。

深拷贝与浅拷贝
  • 浅拷贝:只复制对象的引用地址,而不复制对象本身,新旧对象还是共享一块内存。修改新对象会改变原对象
  • 深拷贝:复制一个一模一样的对象,新对象跟原对象不共享内存。修改新对象不会改变原对象

主要针对于引用数据类型参数说的:

  • 浅拷贝:
    • 对象是基本数据类型,就是将对象值复制给新对象。修改一方不会对另一方产生影响
    • 对象是引用数据类型,就是将引用地址复制给新对象,而不复制对象本身值,新旧对象还是共享同一块内存。修改任意一方都会影响另一方
  • 深拷贝:表示对于对象的克隆,内容的克隆,拷贝的整个对象,不拷贝引用地址;会创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
                      无论嵌套数组对象,不影响原对象;

浅拷贝是地址引用,当其中一个对象变化时,另外一个对象跟着变化;
深拷贝在创建对象时分配内存地址,现对象变化与原对象无关。

var obj = { name:"zhangsan", age:13,no:{ gender:"male"} }
var o = {};

实现深拷贝的方法:
-JSON.parse(JSON.stringify(obj));
-Object.assign(o,obj);
-lodash函数库
-递归
-拓展运算符  var o = {...obj};

3.类型转换

3.1.隐式转换
数据类型转为布尔类型是true值的有布尔类型是false值的有
String非空的字符串“”(空字符串)
Number非零数字(包括无穷大)0和NaN
Object任何对象null
Undefinedundefined

1.字符串和数字:
+隐式将数字转为字符串进行拼接
-*/ % 将字符串隐式转为数字

console.log(10 + '10');//1010
console.log(10 - '5');//5
console.log(10 / '5');//2
console.log(10 * '5');//50
console.log(10 % '5');//0

2.==隐式转换 (undefined派生自null)

  1. number和string进行比较,将字符串转为number进行比较
  2. boolean和number进行比较,将布尔值隐式转为number进行比较
  3. boolean和string进行比较 将两者转为number进行比较

== 相等 将数据类型全部转为number类型再进行值得比较
=== 全等 如果数据类型不一致 直接返回false

console.log(null == undefined);  //true
console.log(null === undefined); //false
console.log(null == 0);          //flase  ==null 和 undefined不进行类型转换
console.log('10' == 10);         //true
console.log('1' == true);        //true
console.log(0 == false);         //true

3.引用数据类型

  1. 将引用数据类型转为基本数据类型调用toString和valueOf (哪一个方法可以转为基本数据类型调用哪一个)
console.log([] + [],typeof ([] + []));     // string
console.log([].valueOf().toString());      // 
console.log({} + [],typeof ({} + []));     //[object Object] string
console.log({}.valueOf().toString());      //[object Object]
console.log({} + {});                      //[object Object][object Object]



/**
 * 可以重写toString和valueOf方法
 * 默认引用数据类型调用toString [object,Object] js自动隐式转换
 * 如果重写了valueOf方法 引用数据类型调用valueOf方法
 * 如果重写了toString 引用数据类型调用toString
 * 如果两者都进行重写 调用valueOf方法 
 */
var obj = {
  name:'zhangsan',
  valueOf(){
    return 10
  },
  toString(){
    return 20
  }
}
console.log({} + 10);  //[object Object]10
console.log(obj + 10); //20 ( valueOf )
console.log(obj + 10); //30 ( toString )
console.log(obj);//{
                 // name: 'zhangsan',
                 //valueOf: [Function: valueOf],
                 // toString: [Function: toString]
                 //}

3.2.显示转换
  1. 将其他数据类型转换为string
//1.使用字符串拼接 
console.log(10 + "",typeof (10 + ""));              //10 string
console.log(null + "",typeof (null + ""));          //null string
console.log(undefined + "",typeof (undefined + ""));//undefined string
console.log(true + "",typeof (true + ""));          //true string
console.log({} + "",typeof ({} + ""));              //[object Object] string
console.log([] + "",typeof ([] + ""));              // string


// 2.调用toString方法  null 和 undefined不能调用,因为他俩没实现包装器函数
//toString()默认十进制,可通过传递参数来改变几进制
var a = 10,b=true;
console.log(a.toString(),typeof (a.toString()));//"10" string
console.log(a.toString(2))	     //"1010"
console.log(a.toString(8))	     //"12"
console.log(a.toString(16))     	//"a"
console.log(b.toString(),typeof (b.toString()));                //true string
console.log(null.toString(),typeof (null.toString()));          //报错
console.log(undefined.toString(),typeof (undefined.toString()));//报错 

// 3.String包装函数 
console.log(String(10),typeof String(10));              //10 string
console.log(String(true),typeof String(true));          //true string
console.log(String(null),typeof String(null));          //null string
console.log(String(undefined),typeof String(undefined));//undefined string
console.log(String({}),typeof String({}));              //[object Object] string
console.log(String([]),typeof String([]));              // string

// number调用toString(进制数)
var a = 10;
console.log(a.toString());  //10
console.log(a.toString(8)); //12 
console.log(a.toString(16));//a
console.log(a.toString(2)); //1010
  1. 将其他数据类型转换为Boolean类型
// 1.Boolean包装器函数
console.log(Boolean(10),typeof (Boolean(10)));            //true boolean
console.log(Boolean('hello'),typeof (Boolean('hello')));  //true boolean
console.log(Boolean(null),typeof Boolean(null));          //false boolean
console.log(Boolean(undefined),typeof Boolean(undefined));//false boolean
console.log(Boolean({}),typeof Boolean({}));              //true boolean
console.log(Boolean([]),typeof Boolean([]));              //true boolean


// !!数据类型
console.log(!!null);        //false
console.log(!!undefined);   //false
console.log(!!10);          //true
console.log(!!"");          //false
console.log(!!{});          //true
console.log(!![]);          //true
console.log(!!('hello'/10));//false
  1. 将其他类型转为number类型
// 1.+       将其他数据类型转为number
console.log(+null,typeof +null);          //0 number
console.log(+undefined,typeof +undefined);//NaN number
console.log(+'hello',typeof +'hello');    //NaN number
console.log(+true,typeof +true);          //1 number
console.log(+{},typeof +{});              //NaN number
console.log(+[],typeof +[]);              //0 number
console.log(+[1,2,3],typeof +[1,2,3]);    //NaN number

// 2.Number()包装函数
console.log(Number(null),typeof Number(null));                //0 number
console.log(Number(undefined),typeof Number(undefined));      //NaN number
console.log(Number('10'),typeof Number('10'));                //10 number
console.log(Number(false),typeof Number(false));              //0 number
console.log(Number(function(){}),typeof Number(function(){}));//NaN number
console.log(Number("+12.1")); //12.1 首位为符号位,其余为为数值,转换为对应的数值 )
console.log(Number("1+2.3")); // NaN 符号位出现在其他位置,解析为NaN )
console.log(Number("0xa"));   //10 如果仅包含十六进制格式,转为为对应的十进制的值)
console.log(Number("010"));   //10【注意】不会当做八进制被解析,结果为10。 
console.log(Number(""));      // 0 空字符串被转换为0 
console.log(Number("123ac")); // NaN 包含其他字符: NaN 

// 3.使用parseInt函数  主要用于取整函数
console.log(parseInt(null),typeof parseInt(null));          //NaN number
console.log(parseInt(undefined),typeof parseInt(undefined));//NaN number
console.log(parseInt(true),typeof parseInt(true));          //NaN number
console.log(parseInt('10.1'),typeof parseInt('10.1'));      //10 number
console.log(parseInt(12.535)); //12
console.log(parseInt(1+2.7));  //3
console.log(parseInt('1+2.7'));//1, 符号位出现在其他位置,保留符号位前面的数值
console.log(parseInt(0xa));    //10,如果仅包含十六进制格式,转为为对应的十进制的值
console.log(parseInt('010'));  //10,【注意】不会当做八进制被解析,结果为10



// 4.使用parseFloat函数   保留小数位
console.log(parseFloat(null),typeof parseFloat(null));          //NaN number
console.log(parseFloat(undefined),typeof parseFloat(undefined));//NaN number
console.log(parseFloat(true),typeof parseFloat(true));          //NaN number
console.log(parseFloat(10.23));//10.23
console.log(parseFloat(10.0)); //10
console.log(parseFloat(10));   //10
console.log(parseFloat(0xa));  //10
console.log(parseFloat(010));  //8

4.操作符:

4.1.算数运算符:+、-、*、/、%
//正常
var num1 = 8;
var num2 = 4;
console.log(num1 + num2); //12
console.log(num1 - num2); //4
console.log(num1 / num2); //2
console.log(num1 * num2); //32
console.log(num1 % num2); //0

//进行数据运算时,除'+'外,其他运算符可以自动将字符串数字隐形转成数字
var num1 = '8';
var num2 = '4';
console.log(num1 + num2,typeof (num1 +num2));//84 string
console.log(num1 - num2);                     //4
console.log(num1 / num2);                     //2
console.log(num1 * num2);                     //32
console.log(num1 % num2);                     //0
4.2.一元运算符:+、-、!、++、- -、delete、typeof、void
+
//1.运
console.log(1 + 2);                              //3
// 2.字符串拼接 
console.log('hello' + 'world');                  //helloworld
// 3.取正 (即本身,不是取绝对值)
var num1 = 6;
var num2 = -6;
console.log(+num1,+num2);                       //6 -6
// 4.将其他数据类型转为number类型 隐式转换 
console.log(+null,typeof (+null));               //0 numder
console.log(+undefined,typeof (+undefined));     //NaN number
console.log(+true,typeof (+true));               //1 number
console.log(+'10',typeof (+'10'));               //10 numder
console.log(+{},typeof (+{}));                   //NaN number
console.log(+[],typeof (+[]));                   //0 numder




-
//1.取负
var num = -4;
var num1 = '8';
console.log(-num);                          //4
console.log(-num1,typeof (-num1));          //-8 number
console.log(-[],typeof (-[]));              //-0 number
//2.将其他数据类型转为number类型 隐式转换 ,同上
console.log(-null,typeof (+null));          //-0 numder
console.log(-undefined,typeof (+undefined));//NaN number
console.log(-true,typeof (+true));          //-1 number
console.log(-'10',typeof (+'10'));          //-10 numder
console.log(-{},typeof (+{}));              //NaN number
console.log(-[],typeof (+[]));              //-0 numder




!// 取反 将其他数据类型转为Boolean类型并进行取反 
//null undefined 0 "" 假值 进行取反true  
console.log(!null);     //true
console.log(!0);        //true
console.log(!undefined);//true
console.log(!"");       //true
console.log(!10);       //false
console.log(!'hello'/10);//0 对"hello"进行取反 false/10 隐式将false转为number0进行运算
console.log(!{});       //flase
console.log(![]);       //flase++ 先自增 再赋值 
后++ 先赋值 再自增
var num = 10;
var res = ++num;//此时num先自增为11,再赋值给res
console.log(res,num);     //11 11
var num = 10;
var res = num++;//此时num为10先赋值给res,在自增为11
console.log(res,num);     //10 11





delete//删除对象或者数组中某一个属性或者某一个元素
var obj = {
  name:'zhangsan',
  age:12
};
console.log(obj);     //{ name: 'zhangsan', age: 12 }
// 删除对象中属性 
delete obj.age;
console.log(obj);     //{ name: 'zhangsan' }

var arr = [1,2,3,4,5];
delete arr[3];
console.log(arr);     //[ 1, 2, 3, <1 empty item>, 5 ]





typeof//返回数据类型
var obj = {
  name:'zhangsan',
  age: 17
};
var arr = [1,2,3,4,5];
console.log(typeof obj, typeof arr); //object object
4.3. 赋值运算符:=、+=、-=、*=、/=、%=
运算符例子等同于
=x = yx = y
+=x += yx = x + y
-=x -= yx = x - y
*=x *= yx = x * y
/=x /= yx = x / y
%=x %= yx = x % y
// = 赋值 
var a = 10;
var b = a;
console.log(a,b);  //10 10

// += -= *= /= %=
var num = 10,c=5;
c+=num;            //c = c+num
console.log(c,num);//15 10
4.4. 比较运算符:= =、= = =、!=、!==、>、>=、<、<=

比较运算符在逻辑语句中使用,以判定变量或值是否相等。通常用于条件判断语句中。
比较运算符的优先级低于算术运算符,高于赋值运算符运算结果一般都为boolean

运算符描述比较返回
==等同x == 8false
两边值类型不同的时候,要先进行类型转换为同一类型后,再比较值是否相等。不同类型都会转化为number 再进行比较x == 5true
x == “5”true
===值相等并且类型相等x === 5true
恒等 的意思,不做类型转换,类型不同的结果一定不等。x === “5”false
!=不相等x != 8true
!==值不相等或类型不相等x !== 5false
x !== “5”true
x !== 8true
>大于x > 8false
<小于x < 8true
>=大于或等于x >= 8false
<=小于或等于x <= 8true

// 基本数据类型比较
console.log(1 == true);  //true
console.log('1' == true);//true
console.log('1' == 1);   //true

// 比较引用数据类型 比较的是引用地址 
var obj = {
  name:'zhangsan'
};//引用地址b1001
var obj1 = {
  name:'zhangsan'
};//引用地址b1002
console.log(obj === obj1);  //false
var obj1 = obj;
console.log(obj == obj1);   //true
console.log(obj === obj1);  //true

// !=  不相等,类似==
// !== 不全等,类似=== 值不相等或者类型不相等
console.log('8' != 9);      //true
console.log('9' !== 9);     //true

// > < >= <=
console.log(5>3);             //true
console.log(2>=3);            //false
console.log(2<=3);            //true
console.log(2<3);             //true
console.log(1 > 'hello');     //false,任何值和NaN作比较结果都是false
//如果符号两侧都是字符串,不会将其转换为数值进行比较,而会分别比较字符串中字符的Unicode编码。
console.log('154698' > '5');  //false
console.log('154698' > + '5');//true,所以在比较两个字符串型的数字时,一定要先转型
//**而在比较字符编码时,是一位一位进行比较的,如果符号两侧第一位一样,则比较下一位**,所以借此可以用来对英文进行排序,而比较中文是没有意义的。
console.log('be' > 'b');      //true
4.5. 逻辑运算符:&&、||、!
/*** &&(同真才真 有假则假) 与 
可应用于任意数值。如果有一个操作数不是布尔类型,逻辑与就不一定返回boolean类型
如果第一个操作数是null,NaN,undefined,false,0,""可被转换为false的值的时候返回该值
 * 条件1 && 条件2 
 * 两个操作数进行相与:     返回假的
 *  如果第一个操作数为真,返回第二个操作数
 *  如果第一个操作数为假,直接返回第一个操作数 */
console.log('hello' && 'world');//world
console.log(null && 'world');   //null
var a =11,b=7;
if(a>10&&b<6){
    console.log('满足条件')
}else{
    console.log('不满足条件')
}                               //不满足条件





/*** || (有真就真 同假才假)或
 满足其中一个操作数或者表达式 
 * 两个操作数进行或操作:     返回真的
 *  如果第一个操作数为真 直接返回第一个操作数
 * 如果第一个操作数为假 返回第二个操作数 */
var a = 11,b=7;
if(a>=10||b<6){
    console.log('满足条件')
}else{
    console.log('不满足条件')
}                               //满足条件
console.log('hello' || 'world');//hello
console.log(null || 'world');   //world





// !取反 null undefiend NaN 0 "" 都是假的值 转为boolean都是false
console.log(!null);         //true
console.log(!0);            //true
console.log(!"");           //true
console.log(!('hello'/10)); //true
console.log(!'hello'/10);   //0
console.log(!{});           //false

4.6. 三目运算符: 表达式?“符合结果”:“不符合结果”
var age = 17;
if(age>=18){
  console.log('成年人')
}else{
  console.log('未成年人');
};//未成年人
var res = age>=18?"成年人":"未成年人";
console.log(res);//未成年人

5.流程控制语句

5.1.条件
  • if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码
  • if…else 语句 - 当条件为 true 时执行代码,当条件为 false 时执行其他代码
  • if…else if…else 语句- 选择多个代码块之一来执行
  • switch 语句 - 选择多个代码块之一来执行
function exchange(num) {
  if (num && typeof num == 'number') {
    //typeof num=='number' && 0<num and num<=10
                  if (num > 0 && num <= 10) {
                    if (num == 1) {
                      result = "壹";
                    } else if (num == 2) {
                      result = "贰";
                    } else if (num == 3) {
                      result = "叁";
                    } else if (num == 4) {
                      result = "肆";
                    } else if (num == 5) {
                      result = "伍";
                    } else if (num == 6) {
                      result = "陆";
                    } else if (num == 7) {
                      result = "柒";
                    } else if (num == 8) {
                      result = "捌";
                    } else if (num == 9) {
                      result = "玖";
                    } else {
                      result = "拾";
                    }} 
                  else if(num > 10) {
                    result = "请输入不大于10的数字";
                  } else{
                    result = "请输入不小于0的数字";
                  }} 
  else if (num == 0) {
    result = "零";
  } else {
    result = "请输入数字";
  }
  console.log(result);
}
exchange(0);    //零 else-if
exchange(-120); //请输入不小于0的数字
exchange(100);  //请输入不大于10的数字
exchange('as'); //请输入数字
exchange();     //请输入数字
console.log(typeof 0)//number
var num = 0;
if(num){
  console.log('满足条件')
}else{
  console.log('不满足条件');
}

做等值比较的分支控制建议使用switch,非等值的判断建议使用If

var day = new Date().getDay();//0-6 1-5周一周五 6周六 0周日  1-12月  0-11月
switch (day) {
  case 0:
    console.log('今天是周日');
    break;
  case 1:
    console.log('今天是周一');
    break;
  case 2:
    console.log('今天是周二');
    break;
  case 3:
    console.log('今天是周三');
    break;
  case 4:
    console.log('今天是周四');
    break;
  case 5:
    console.log('今天是周五');
    break;
  case 6:
    console.log('今天是周六');
    break;
}//今天是周二

var day = new Date().getDay();//0-6 1-5周一周五 6周六 0周日  1-12月  0-11月
switch (day) {
  case 5:
    console.log('今天是周五');
    break;
  case 6:
    console.log('今天是周六');
    break;
  default:
    console.log('期待周末');
}//期待周末

default 关键词来规定匹配不存在时做的事情:

注意:

  1. case代码块中break不能省略
  2. default可以放到代码任意位置,break不能省略,最后位置可以省略break;
  3. 变量与常量对比使用”===“
5.2.循环
  • for - 循环代码块一定的次数
  • for…in - 循环遍历数组或对象的属性
  • while - 只要指定条件为 true,循环就可以一直执行代码块。
  • do/while - 该循环会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。
// for(开始条件;结束条件;迭代条件){
//   循环体
// }
// 使用for循环 循环遍历数组每一个元素 
for(var i=0;i<arr.length;i++){
  console.log(i,'数组元素对应的下标',arr[i],'数组每一项')
}
//0 数组元素对应的下标 1 数组每一项
//1 数组元素对应的下标 2 数组每一项
//2 数组元素对应的下标 3 数组每一项
//3 数组元素对应的下标 4 数组每一项
//4 数组元素对应的下标 5 数组每一项
//5 数组元素对应的下标 6 数组每一项



//for(自定义变量名 in 数组/对象){
//​	循环体
//}
//“自定义变量名”用来指定是数组的元素索引,也可以是对象的属性

//1.循环数组
var arr=[10,'aa',20,30,40];
//for(var i=0;i<arr.length;i++){
//  console.log(i+"--"+arr[i]);
//}
for(var key in arr){
  console.log(key+"--"+arr[key]);
}//0--10  1--aa  2--20 3--30  4--40

//2.循环对象属性:
var obj = {
  name:"briup",
  age:12,
  salary:10000
};
/*两种方式访问属性:
objectName.propertyName或者objectName["propertyName"]*/
console.log(obj.name);//briup
console.log(obj["age"]);//12
console.log(obj.salary);//10000
for(var key in obj){
  console.log(key+"--"+obj[key]);
}//name--briup  age--12  salary--10000






// while(条件){
//   循环体
//   迭代条件
// }
// 使用while循环求1-100和
var i=0;result=0;
while(i<=100){
  result+=i;
  i++
}
console.log(result)//5050





// do{
//   循环体
//   迭代条件
// }while(条件) 
//无论满不满足条件 do while循环至少会执行一次
var i=101,result=0;
do{
  result+=i;
  i++
}while(i<=100)
console.log(result);//101

关键字break,continue

//如果想在所有迭代前退出,即可使用break。当执行break后,会立即跳出循环体。
//关键字 continue与break不同的是,continue不会跳出循环。而是立即结束当前循环,进入下一次循环。
for(var i=0;8;i++){
  console.log(i);
  if(i==5){
    break;
  }
}//0 1 2 3 4 5

实例:

// 求1-100偶数和
var result=0;
for(var i=1;i<=100;i++){
  if(i%2==0){
    result+=i
  }
}
console.log(result)//2550
  • 冒泡排序
/**
 * 冒泡排序核心思路:
 *  1.比较相邻两项,将大的往后放,将小的往前放 交换位置
 *  2.每一轮比较后最大的数在最后面 
 *  3.由于最后一个数是最大得数 不参与这一轮比较
 *  外部总共比较多少轮  每一轮比较次数 
 * 
 *  第一轮:20 18 [18,20,27,19,35]
 *         20 27  [18,20,27,19,35]
 *         27 19  [18,20,19,27,35]
 *         27 35  [18,20,19,27,35]
 *  第二轮  18 20 [18,20,19,27,35]
 *          20 19 [18,19,20,27,35]
 *          20 27 [18,19,20,27,35]
 *  第三轮:  18 19 [18,19,20,27,35]
 *          19 20 [18,19,20,27,35]
 *  第四轮  18 19 [18,19,20,27,35]
 */
var arr = [20,18,27,19,35];//进行排序 从小到大排序 
function bSort(arr){
  // 控制外面比较轮数 
  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]){
        //交换位置  把后一项存起来
        var temp = arr[j+1];
        // 把小得往前放 
        arr[j+1] = arr[j];
        arr[j] = temp;
      }
    }
  }
  return arr
}
console.log(bSort(arr));//[ 18, 19, 20, 27, 35 ]
  • 递归就是函数自身调用自身
    使用场景:深拷贝 斐波那契数列 阶乘
/**
 * 
 * 6得阶乘  6 * 5 * 4 * 3 * 2 *1 
 * 跳出条件
 */
/** 
 * 1阶乘 1 fc(1)
 * 2 2*fc(1) = fc(2)
 * 3 3*fc(2)
 * 4 4*fc(3)
 * 5 5*fc(4)
 * 6 6*fc(5)
*/
function fc(n) {
  // 设置跳出条件
  if(n==1){return 1}
  return n * fc(n - 1)
}
var res = fc(6);
console.log(res)//720

6.面向对象

6.1内置对象及函数
  • 引用类型:有自己内置的方法,也可以自定义其他方法用来操作数据
  • 基本数据类型:不能像引用类型那样有自己的内置方法对数据进行更多的操作。

为了便于操作基本数据类型,ES提供了3个特殊引用类型(基本包装数据类型):Boolean, Number, String。

  • 基本包装类型/特殊引用类型/包装器函数:和其他引用类型一样,拥有内置的方法可以对数据进行额外操作。

为什么一些基本数据类型可以调用方法和属性 ? (js做了一些事情)
1.js进行自动装箱 将基本数据类型包装成特殊引用数据类型的实例 var str = new String(‘hello’);
2.可以调用原型方法和属性 str.split(“”) String.prototype.xxx
3.js进行自动拆箱 将特殊引用数据类型转为基本数据类型
字符串调用方法 String.prototype.xxx

6.2拓展库
  1. Moment.js
    moment官网是个js日期处理类库
    node.js中使用
// 在当前目录下使用node安装moment库
npm install moment --save

// 模块化导入moment
var moment = require('moment');
// 设置本地语言为中文
require('moment/locale/zh-cn')
moment.locale('zh-cn');

// 根据对应的格式输出当前时间
console.log(moment().format('MMMM Do YYYY, h:mm:ss a'));//八月 30日 2021, 11:07:46 晚上

浏览器中使用

// 可以下载js文件,也可使用对应的cdn文件,bootcdn
<script src="moment.js"></script>
<script>
    moment().format();
</script>
  1. lodash
    lodash官网是一个一致性、模块化、高性能的 JavaScript 实用方法工具库

为什么选择 Lodash ?
Lodash 通过降低 array、number、objects、string 等等的使用难度从而让JavaScript 变得更简单。

Lodash 的模块化方法 非常适用于:
遍历 array、object 和 string 对值进行操作和检测 创建符合功能的函数

浏览器环境安装:
<script src="lodash.js"></script>

通过 npm安装:
$ npm i -g npm
$ npm i --save lodash


//引入lodash
var _ = require('lodash');
// 实现一个对象深拷贝 cloneDeep
var obj = {
    name: 'terry',
    age: 15
}
var obj1 = _.cloneDeep(obj);
console.log(obj1, obj, obj1 === obj);//{ name: 'terry', age: 15 } { name: 'terry', age: 15 } false



//chunk(arr,size) 将数组拆分成区块 将size个数组元素拆分成一个区块
var arr = [1,2,3,4,5,6];
console.log(_.chunk(arr,3));		//[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]


//compact 过滤假的值数组元素 返回真值数组元素组成新数组
var arr = [1,"",false,null,undefined,'hello',0];
console.log(_.compact(arr));		//[ 1, 'hello' ]


// 从头开始删除数组元素 删除数组元素个数 默认为1
var arr = [1,2,3,4,5];
console.log(_.drop(arr,3));			//[ 4, 5 ]


// uniq 去重
var arr = [1,2,3,4,3,2,1];
console.log(_.uniq(arr));			//[ 1, 2, 3, 4 ]
6.3四大设计模式

为了批量创建对象 ,为了实现继承

//平常创建对象可用这两种方法,但对象多的话,这样创建很麻烦,从而有了以下四种模式
var obj = {
name:'terry',
age:12,
gender:'male',
sayName:function(){}
};
//或
var obj = new Object();
obj.name ='lisi';
obj.age =12;
obj.gender='male';
6.3.1工厂模式

工厂模式 :使用工厂函数批量创建对象
优点:可以批量创建对象
缺点:无法区分种类,创建对象都是Object的实例 , 方法冗余(无法区分声明在公共区域的方法是给谁使用的,因为谁都可以调用)

var sayName = function(){
  console.log(this.name)
};//b1001
//函数是引用类型,调用会在堆区中占内存
//若放在下面的函数里,每调用一次就会占一个内存,调多了就会占很多内存会造成冗余,降低效率。所以将它声明成一个公共的方法,这样只用消耗一个内存
function Person(name,age,gender){
  return {
    name:name,
    age:age,
    gender:gender,
    sayName:sayName
  }
}


var p1 = Person('terry',12,'male');
var p2 = Person('larry',18,'female');
console.log(p1,p2);	//{name: 'terry',age: 12,gender: 'male',sayName: [Function: sayName]}	{name: 'larry',age: 18,gender: 'female',sayName: [Function: sayName]}
p1.sayName();		//terry
p2.sayName();		//lerry
console.log(p1.sayName===p2.sayName);//true,此处比的是引用地址,而不是值

function Animal(){
  return {
  }
}
var a1 = Animal();		//a1应该是animal的实例,但在这还是object的实例
//(输出的结果{}前没东西,就是属于Object了)
console.log(a1);		//{}
6.3.2构造函数模式

优点:可以批量创建对象、可以区分种类
缺点:方法冗余(同工厂模式)

  • new关键字作用
    1.创建一个构造函数的实例对象 var p1 = Person{};
    2.将this指向实例对象 this—>p1
    3.执行函数体代码 this.name = name;
    4.返回实例对象 return p1
var sayName = function(){ console.log(this.name)}
function Person(name,age,gender){
//  new Object() new Array()构造函数名必须大写
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = sayName;
}
//构造函数要用new调用(此处要用构造函数模型,所以用new调用)
var p1 = new Person('terry',12,'male');
var p2 = new Person('larry',18,'female');
console.log(p1,p2);				//Person {...}    person{...}
//因为new作用的1,才造成了该结果。(像这个输出结果{}前有person就不属于object了,而是属于person实例)
console.log(p1.sayName===p2.sayName);	//true
p1.sayName();							//terry
p2.sayName();							//lerry

function Animal(){

}
var a1 = new Animal();
console.log(a1);
6.3.3原型模式 (不单独使用)

:将实例所有属性和方法全部写在原型对象中
构造函数不做任何处理
优点:可以区分种类,可以批量创建对象 ,方法不冗余
缺点:所有实例方法和属性一样

function Person(){};
Person.prototype.name = 'lisi';
Person.prototype.age = 12;
Person.prototype.gender = 'male';
Person.prototype.sayName = function(){
  console.log(this.name);
};
Person.prototype.firends = [];
var p1 = new Person();
var p2 = new Person();
console.log(p1,p2);												//Person {} Person {}
console.log(p1.name,p2.name,p1.sayName(),p2.sayName()); 		//lisi lisi undefined undefined
p1.firends.push('tom');//往p1加tom,p2也会加
console.log(p1.firends,p2.firends);								//[ 'tom' ] [ 'tom' ]
6.3.4组合模式 (构造函数模式 + 原型模式) 常用

实例私有属性和私有方法全部放在构造函数
公共方法和属性放在原型对象中

function Person(name,age,gender,weight){
  // 私有属性
  this.name = name;
  this.age = age;
  this.gender = gender;
  // 私有方法
  this.weight = function(){
    console.log(weight)
  }
}

// // 公共方法
// Person.prototype.sayName = function(){
//   console.log(this.name);
// }
// // 公共属性
// Person.prototype.type = 'Person实例';

// 简化上述代码
Person.prototype = {     //此时person.prototype={},将构造者改成了object
  constructor:Person,    // 所以要将构造者再改回来为Person 
  sayName:function(){
    console.log(this.name)
  },
  type:'Person实例'
};

var p1 = new Person('terry',16,'male','40kg');
var p2 = new Person('larry',18,'female','60kg');
p1.sayName();            //terry
p2.sayName();            //lerry
console.log(p1.sayName === p2.sayName,p1.type === p2.type);   //true  true 因为都写在原型对象中,都属于公共的
console.log(p1.type,p2.type);                                 //Person实例 Person实例
p1.weight();             //40kg
p2.weight();             //60kg
console.log(p1.weight === p2.weight);           //false
console.log(p1.constructor,p2.constructor);     //[Function: Person] [Function: Person]


// instanceof 检测实例 在不在原型链中 ,是否是构造函数实例
console.log(p1 instanceof Person);          //true
console.log(p1 instanceof Object);          //true
console.log(p1 instanceof Array);           //false
console.log(Person instanceof Object);      //true

function Animal(){};
console.log(Animal instanceof Object);      //true

//所有的引用数据类型都是object数据类型的子类型




6.4 继承

ES5继承:
1.借用构造函数继承(经典继承)
2.原型链继承
3.组合继承 1 + 2

//3.组合
function Animal(type,weight,age,length){
  this.type = type;
  this.weight = weight;
  this.age = age;
  this.length = length;
};
Animal.prototype = {
  constructor:Animal,
  sayType:function(){
    console.log(this.type);
  }
}

// 创建子构造函数
function Dog(type,weight,age,length,name,color){
  // 调用父构造函数继承属性 Animal()    父构造函数this指向父构造函数创建实例 
  //1. 借用构造函数继承 经典继承 将父构造函数this指向子构造函数实例
  Animal.call(this,type,weight,age,length);//直接调用Animal()此时this还指向Animal,而以后要执行dog里的代码,调用d1里面的数据,所以要将this指向修改为dog
  this.name = name;
  this.color = color;
};

// 2.原型链继承 破坏原型 
Dog.prototype = new Animal();// 将子构造函数原型对象 指向 父构造函数实例
Dog.prototype.constructor = Dog;// 将子构造函数原型构造者 改为 子构造函数
Dog.prototype.sayType = function(){
  console.log(this.type,'子构造函数原型方法');
}
var d1 = new Dog('狗','40kg',10,'40cm','可乐','白色');
console.log(d1);                  //Dog {type: '狗',weight: '40kg',age: 10,length: '40cm',name: '可乐',color: '白色'}
d1.sayType();                     //狗 子构造函数原型方法
console.log(d1.constructor);      //[Function: Dog]

子类实例访问方法或者属性:
1.先去自身寻找
2.自身没有向子构造函数原型对象中去寻找
3.若还找不到,就破坏原型链 向父构造函数原型对象中去寻找

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值