js一般规则
前端面试
- 0.1 + 0.2 是否== 0.3
- js严格区分大小写
js可以不写分号,但
①多行代码写一行必须有分号。<br />②**标准写法:一行代码写一行,且每一行都要加分号**。
如果出错,F12->console查看错误,进一步打开source->添加断点和要监视的变量(右键)->f5进入调试模式->f9开始调试->同时不断查看console和source。
- 可正常计算的范围:小数点前16位、小数点后16位
- js语言是单线程的
- 因此JavaScript的所有网络操作、浏览器事件都只能异步执行。
- js语言是异步编程语言
- 异步编程语言不会像同步编程那样一个事件一个事件的执行,比如在setTimeOut设置的等待时间内CPU会继续执行别的任务,而不会完全停止。
- 一个等号是赋值操作,==先转换类型再比较,===先判断类型,如果不是同一类型直接为false。
js的ASI机制(Automatic Semicolon Insertion)
什么是ASI
按照 ECMAScript 标准,一些 特定语句(statement) 必须以分号结尾。分号代表这段语句的终止。但是有时候为了方便,这些分号是有可以省略的。这种情况下解释器会自己判断语句该在哪里终止。这种行为被叫做 “自动插入分号”,简称 ASI (Automatic Semicolon Insertion) 。
本质
js的书写位置
- 内嵌式js
- 直接写在标签里,一般是写在body标签的下面,目的是加快页面的响应速度。(存疑)
- 外部式js
- 用script标签引入外部js:
<script src="1.js"></script>
- 引用了外部式js,里面写的js会被浏览器自动忽略
<body>
<p onclick="alert('有效')">点我</p>
</body>
<script src="exer-1.js">
document.write('无效')
</script>
<script>
//重建script有效
document.write('有效')
</script>
- 行内式js
- 可以写在标签的onclick属性上
<p onclick="alert('不听话啊')">不要点我</p>
- 可以写在a标签的href属性上
<a href="javascript:alert('别走')";
- 不推荐,因为造成了html和js代码耦合。
作用域
- 定义
- 作用域表示变量的作用区域
- 分类
- 全局作用域
- 存在位置
- 直接编写在 script 标签之中的JS代码
- 一个单独的js文件中
- 存在时间
- 在页面打开时创建,在页面关闭时销毁
- 特点
- 全局作用域中有一个全局对象window(代表的是一个浏览器的窗口,由浏览器创建),在全局作用域创建的变量都会作为window对象的属性保存;所有创建的函数都会作为 window 对象的方法保存。
- 全局变量
- 定义
- 在全局作用域由var创建的变量
- 在函数作用域直接赋值的变量(不建议使用)
- 存在时间
- 全局变量只有在浏览器关闭的时候才会销毁,比较占用内存资源
- 特点
- 全局变量在任何地方都可以使用
- 定义
- 存在位置
- 函数作用域/局部作用域
- 存在位置
- 函数范围
- 存在时间
- 调用函数时创建函数作用域,函数执行完毕销毁
- 特点
- 每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的
- 局部变量
- 定义
- 在函数作用域由var声明的变量
- 函数的形参也是局部变量
- 存在时间
- 只能在函数内部使用,当其所在代码块被执行时,会被初始化;当代码块执行完毕就会销毁,因此更节省节约内存空间。
- 特点
- 全局作用域无法访问局部变量
- 定义
- 存在位置
- 作用域链
- 定义
- 内部函数访问外部函数的变量,会采用链式查找的方法,这种结构就叫作用域链。
- 原则
- 就近原则 ```javascript //知识点1:全局变量会作为window对象的属性保存,但局部变量不会被作为局部对象的属性保存。 var num = 10; console.log(window.num);//10
- 定义
- 全局作用域
func = function(){ var num = 20; } func(); console.log(window.num);//10 console.log(func.num);//undefined
```javascript
//知识点1:全局变量会作为window对象的属性保存,但局部变量不会被作为局部对象的属性保存。
//知识点2:作用域链:就近原则
var num = 30;
var fn = function(){
var num = 10;
fn.num = 20;
(function(a){
console.log(a);
})(num)//会一级一级往上面找,num = 10
}
fn();//10
console.log(fn.num);//20
//-----对比-------
//知识点:局部作用域可以访问全局变量
var num = 30;
var fn = function(){
fn.num = 20;
(function(a){
console.log(a);
})(num)//会一级一级往上面找,num = 30
}
fn();//30
console.log(fn.num);//20
- 块级作用域
- 存在位置
- ES6之前:()即大括号 ——-> 使用一个立即执行的匿名函数 ```javascript
- 存在位置
function test(){ (function(){ for(var i=1;i<5;i++){ alert(i); } })(); console.log(i);//Uncaught ReferenceError: i is not defined } test();
- 由let创建块级作用域
<a name="C9Vpr"></a>
## js的字面量和变量
---
<a name="mbjxb"></a>
### 字面量
- 字面量就是常量,一般是全大写表示 `var INF = 999999999;`
<a name="u2rFR"></a>
### 变量
- 全局变量和局部变量
- 全局变量
- 定义
- 除了在函数里面**用var定义的变量**都是全局变量
- 生命周期
- 在页面打开时创建,关闭时销毁
- 和windows对象一样
- 在js里,所有的全局变量都是js的window对象的属性;所有的全局函数都是js的window对象的方法
- windows对象:js里面给我们创建好了的对象,表示浏览器的一个窗口
- 局部变量
- 定义
- 函数里面**用var定义的变量**是局部变量
- 生命周期
- 挂靠在函数上面
- 在函数使用时被创建,使用完时销毁
- 函数里面使用变量的时候,优先在函数里面找,如果有就用,如果没有就向上找。
- 如何在函数里面使用全局变量?-通过windows对象来访问,比如windows.a
```javascript
function hello(){
console.log(a);
var a = 11;//局部变量
d = 15;//全局变量
}
hello();
console.log(d);
for(var i = 0; i < 3; i++){
console.log(i);//i是全局变量
}
- 默认初始值
- 变量如果只声明不被赋值,初始值是undifined,console.log(变量)会看到undifined
变量命名
控制台查看数据类型
`console.log(typeof str);`
基本数据类型
基本数据类型都可以用Object封装一下,变成一个对象,而且它们会和function对象一样,比普通object功能更强大,因为它们本身是有含义的。
- 它们的原型会先是Number/String/function自己的构造函数….,然后才是Object
- 即使这个对象有了很多别的属性,但对于本身的含义,可以和不作为Object的时候一样 直接使用
var num = new Object(123);
num.age = 13;
var n = num + "123";
console.log(n);//123123(打印出来的没有引号,查看类型肯定是字符串)
- String:字符串
var str = '我爱你,我的家!';
console.log(typeof str);//--->string
var char = str[0];
console.log(typeof char);//--->string
- 单引号和双引号都可以,如果是单引号就只能在内部用双引号,反之是双引号就只能在内部用单引号,也可以用转义字符:
var str = "她说:\"我爱你,我的家。\"";
- 转义符 :\(反斜杠) 、\t(制表符)、\n(换行符)、\”(双引号)、\’(单引号)、\r(回车)
- 制表符,理解为tab,两个字符缩进
- var char = str[0],的字符类型还是string
- Number:数字—->整数、小数(浮点数) ```javascript var num1 = 123; console.log(typeof num1);//number
var num2 = Number.MAX_VALUE; console.log(num2);//1.7976931348623157e+308
var num3 = Number.MIN_VALUE; console.log(num3);//5e-324
var num4 = Infinity;//正无穷大 console.log(num4);//Infinity
var num5 = -Infinity;//负无穷大 console.log(num5);//-Infinity
- Number 类型是一个 双精度 64 位二进制格式 IEEE 754 值(-(2^53 − 1) 和 2^53 − 1 之间的数字)
- js的小数做计算的时候,可能会出现不精确的情况。
原因是:在10进制里面,1/3是没有办法被精确表示的,0.3333,在二进制里面,1/10也没有办法被精确表示,是一个无限循环小数。<br /> ** 解决方法:先转化成整数做计算,再转化为小数**
3. Boolean:布尔值
```javascript
var bool = true;
console.log(typeof bool); //boolean
- Null:空值
var v1 = null;
console.log(typeof v1); //object
- Null表示是空值
- Null这个数据类型里面只有一个值,就是null
- 这个null值表示的是一个空的对象
- Undefined:未定义(字面意思)
var v;
console.log(typeof v);//undefined
它表示变量定义/声明了没有赋值,但是变量肯定定义了!!,没有定义会报错。
引用数据类型
object
基本概念
定义
- 对象:理解为一种容器,这个容器里可以装各种数据类型。
- 性质
- 在js中,除了5种基本的数据类型,其它都是对象。
- 对象分类
- 内建对象:内部已经创建好的对象(内部就是es,es就是js的一个标准)
- 比如:Math、Number、String、Function、Array…..
var a = Math.sqrt(9);
- 宿主对象:表示js运行环境提供的对象(运行环境:浏览器)
- 比如:console、document、window…..
- 自定义对象:我们自己定义的对象 ```javascript //使用Object构造函数创建对象 var human = new Object();//在堆中new出一片内存空间,并生存一个内存地址赋给引用的变量 human.name = “张十三”; human.age = 18;
- 内建对象:内部已经创建好的对象(内部就是es,es就是js的一个标准)
console.log(typeof human);//object
<a name="Zx3IB"></a>
#### new运算符
1. 创建一个新对象;
1. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
1. 执行构造函数中的代码(为这个新对象添加属性) ;
1. 返回新对象。
<a name="MMYHb"></a>
#### 对象的基本操作
1. 点号版
1. 创建
`var 对象名 = new Object();`
2. 新增、修改
`对象名.属性名 = 属性值;`
3. 访问
`对象名.属性名`<br />ps:访问对象没有的属性,不会报错,会返回undefined
4. 删除
`delete 对象名.属性名`
2. 中括号版
- 底层是中括号
```javascript
human[123] = 'aaa';
console.log(human[123]);
//这是点号不能进行的操作,因为123是不符合规范的变量名。
//因此中括号可以进行一些特殊的字符操作
var a = 123;
console.log(human[a]);
a = 456;
console.log(human[456]);
//中括号的属性名可以是变量,这也是点号不能进行的操作
创建对象的字面量形式
JSON的属性名称要求以“双引号”包裹,所有只有*行是json格式的字面量表达
var zhubajie = {};//创建空对象就这么写的,效率高
zhubajie.name = "猪八戒";
zhubajie.age = 9;
//主要是下面这种,因为效率高
var zhubajie = {name:"猪八戒",age:"9"};//属性名加不加引号都可以
var zhubajie = {'name':"猪八戒",age:"9"};//单引号√
var zhubajie = {"name":"猪八戒",age:"9"};//双引号√ *
var zhubajie = {'name':"猪八戒",age:9};//去掉属性值的引号,更改了其数据类型
json
- 全称
- javascripte object notation(js的对象符号)
- 作用
- 通信,前后端交互(比xml多)
语法:
作用:封装代码,便于复用。
- 本质:使用函数封装代码
```javascript
//1、定义一个有名字的函数
var createPeople = function(name, age){
//2、该函数内部要结合传参,定义一个初始对象
var people = {
name:name,
age:age,
introduction:function(){
} } //3、返回这个对象 return people; } var xiaozhang = createPeople(“xiaozhang”, 21); var xiaowang = createPeople(“xiaowang”, 18);console.log("我是" + this.name + ",我今年" + this.age + "岁了。")
xiaozhang.introduction();//我是xiaozhang,我今年21岁了。 xiaowang.introduction();//我是xiaowang,我今年18岁了。
<a name="caEl4"></a>
#### 用构造函数创建对象
- 好处
- 可以标注对象
- 特点
- 创建时:**函数名的首字母大写(约定俗成)**
- 使用时:必须使用new关键字
- 本质
1. 它会创建一个空对象{};
1. 链接空对象的原型到构造函数的原型;
1. 将空对象作为this的上下文,并执行构造函数;
1. 返回this
```javascript
function myNew(f,...args){
//创建空对象
let obj=Object.create(null);
//将空对象指向构造函数的原型链
Object.setPrototypeOf(obj,f.prototype);
//将this绑定给obj,并执行函数
let r=f.apply(obj,args);
//返回函数值,若不是对象返回obj
if (typeof(r) == "object"){
return r;
}else{
return obj;
}
}
//构造函数
function f(name,age){
this.name=name;
this.age=age;
}
var pa=myNew(f,'张三','26');
console.log(pa)
- 怎么判断一个对象是不是一个类的实例
- instanceof
- 不足
- 浪费资源:每次使用构造函数创建对象的时候,构造函数中的方法(比如introduction方法)都会被创建,如果我们要创建10000个对象,那么introduction方法也就会创建10000次。
- 怎么解决?
- 法一:把构造函数中的方法放到全局,这样虽然解决了资源浪费的问题,但是会污染全局的方法,所以不推荐。
- 法二:原型(即定义对象的prototype) ```javascript function People(name, age){ this.name = name; this.age = age; this.introduction = function(){ console.log(“我是” + this.name + “,我今年” + this.age + “岁了。”) } } var xiaozhang = new People(“xiaozhang”, 21); var xiaowang = new People(“xiaowang”, 18); console.log(xiaozhang);
xiaozhang.introduction(); xiaowang.introduction();
console.log(xiaowang instanceof People);//true
<a name="PO9i3"></a>
#### 对象里面套对象
- 对象里面还可以是对象
- 本质:对象里的一个属性名,可以对应任何一种数据类型
```javascript
var baba = new Object();
baba.name = '张力';
baba.age = '48';
var nver1 = new Object();
nver1.name ='Anna';
nver1.age = '20';
baba.haizi1 = nver1;
console.log(baba.haizi1.age);
对象里是否有这个属性
- 针对单个属性
- 只检查能不能访问到,不能检查到底属不属于这个对象
遍历属性-包含继承属性在内的所有可枚举的属性名 ```javascript var zhubajie = new Object(); zhubajie.name = “猪八戒”; zhubajie.age = 18; zhubajie.saorao = function(){ console.log(“我骚扰了嫦娥,被贬下凡间”); }console.log('age' in nver1);//true
console.log(age in nver1);//报错:Uncaught ReferenceError: age is not defined
//打了引号才是属性,没有引号代表一个变量,而age变量根本没有定义
var age = 'age';
console.log(age in nver1);//true
for(var i in zhubajie){ console.log(i);//i分别是 “name”、”age”、”saorao” console.log(zhubajie[i]); }
获取属性-只包含自身的可枚举的属性名
```javascript
var obj1 = {
name:"anna",
age:13
}
var arr = Object.keys(obj1);
//arr = ["name", "age"]
对象里的函数
对象里的函数一般称为方法,但实际是一回事,都是封装代码,便于代码的复用
//1、使用Object构造函数创建对象-->赋值表达式
//好像这就是,用“赋值表达式创建函数”这种方法存在的意义,因为要把它赋给一个对象/对象属性。
var zhubajie = new Object();
zhubajie.name = "猪八戒";
zhubajie.age = 18;
zhubajie.saorao = function(){
console.log("我骚扰了嫦娥,被贬下凡间");
}
zhubajie.saorao();
zhubajie.benpao = new Function("console.log('我在奔跑')");
zhubajie.benpao();
//2、json形式/字面量形式
var action = {
func:function(){
console.log("我是func");
},
name:'猪八戒'
}
action.func();//"我是func"
function
写法
function 函数名([参数1, 参数2, …]){
...函数体...<br /> }
```javascript //1、用声明的方式创建对象 function minOfTwo(a, b){//形参:函数里面定义的变量 if(a < b){ console.log(a); }else{ console.log(b); } }
minOfTwo(1,2);//实参:(给形参注入灵魂)使用时传递给函数的实际参数
<a name="ZmGCF"></a>
#### 实参和形参
- 形参:函数里面定义的变量,用于接收实参传递过来的值。
- 实参:(给形参注入灵魂)使用时传递给函数的实际参数
- 联系
- 实参会一一传递给形参,多余的参数会被丢弃
- 如果形参没有接收到实参,就是undefined(相等于声明不赋值)
<a name="r3MIe"></a>
#### 返回值
- return后面的语句不会被执行
- 如果不写return语句或者“return;”(即不自定义任何返回值),会返回undefined
<a name="MGfCn"></a>
#### 函数也是对象
有对象的一切特点,只不过是功能更加强大的对象,函数里面可以封装代码
```javascript
function hello(){
console.log("欢迎");
}
hello.age = 13;
console.log(hello);//打印函数代码
console.log(hello.age);//13
console.log(hello.name);//hello (name是“通过声明定义的函数”的自带属性)
创建函数的其它几种写法
- 不论用以下哪种方法创建函数,当执行到这一行时,编译器都只会先声明它(一整坨),当确定执行的时候才会进入执行。 ```javascript //2、用创建对象的方式创建函数 //用的很少,太麻烦了 var sumTwo = new Function(‘num1’, ‘num2’, ‘return num1 + num2’); console.log(sumTwo(10, 30));//40
var fun1 = new Function(“console.log(‘我是fun1’)”); fun1();//我是fun1
//3、(属于函数表达式)用匿名函数-用括号代表一个函数对象 (fucntion(){} …. )
//4、(属于函数表达式)用匿名函数-赋值表达式创建一个函数对象 var fun2 = function(){ //匿名函数 console.log(“我是fun2”); }; fun2();//我是fun2
//5、(属于函数表达式)用匿名函数-在用字面量表达的对象内,创建方法 var obj = { func1:function(){ …. }, }
//6、用原型创建 var obj1 = Object.create(原型);
//7、箭头函数
<a name="kCycv"></a>
#### 匿名函数
- 定义:没有名字的函数
- 调用:直接在匿名函数后面加上小括号
- 函数名本身代表的就是函数体,所以这种调用方式和 func()等效
```javascript
(function (){
console.log("你好");//你好
})();
(function (a){
console.log(a);//10
}(10));
//注意立即执行函数可以有名字
(function sayHi(){
console.log("你好");//你好
})();
- 应用 ```javascript //1、用赋值表达式创建一个方法 var func1 = function(){ };
//2、在字面量表达的对象里创建方法 var action = { func2 : function(){ console.log(“我是在json里创建的函数”); } }
//3、做回调函数:由我们创建,但没有直接调用,而函数会在某个时候或者某种条件执行的函数。 //(定时器函数是一个典型的回调函数) setInterval(function(){ console.log(“我是回调函数”); },1000);
//4、事件 var sub = document.querySelector(“#sub”); sub.onclick = function(){ console.log(“按钮被点了”); }
//5、作为函数的返回值 (可能是闭包) function bigFunc(){ console.log(“我是bigFunc”); return function(){ console.log(“我是被作为返回值的函数”); } } console.log(bigFunc);//打印所有代码 console.log(bigFunc());//最终是要打印return值,但是会先执行bigFunc,再打印return console.log(bigFunc()());//最终是要打印return值,但会先执行bigFunc->执行smallFunc->打印return
bigFunc()();//会先执行bigFunc->执行smallFunc
//或者,也可以这么调用👇 var box = bigFunc();//会执行bigFunc box();//执行smallFunc
//6、使用立即执行的匿名函数作为块级作用域 (可能是闭包) function bigFunc(){ (funciton(){ var smallStr = “我是smallFunc”; })(); console.log(smallStr);//报错 }
<a name="c3lAX"></a>
#### 回调函数
- 回调函数可以作为一个参数在另一个函数中被调用
- 实例:
```javascript
function peaceAndLove(){
console.log("和好了");
}
function quarrel(){
console.log("吵架了");
setTimeout(peaceAndLove, 1000);
}
quarrel();
函数和变量的预解析
变量的预解析
- 在js中,使用var关键字定义的变量会声明提前,但是不会提前赋值
- 实质是
- 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。(所以说,也要当前作用域开始作用的时候才会被声明)
- 如果作用域是全局,解析器会把var关键字声明的变量加到windows对象的属性里面;如果作用域是函数,解析器会把var关键字声明的变量加到this对象的属性里面。
- 特点:在其作用域之上只有声明,到该语句,才有赋值。
console.log(a);//exer-1.html:14 Uncaught ReferenceError: a is not defined
```javascript function hello(){ console.log(a);//undefined var a = 11;//(加到了函数的windows对象的属性里面) } hello();console.log(a);//undefined
var a = 10;(加到了全局的windows对象的属性里面)
**函数的预解析**
- 在js中,使用 **函数声明 **的方法定义函数会声明提前。
- 实质是:
- 函数的声明(那一坨)会被提升到当前作用域的最上面,但是不会调用函数。
- 如果作用域是全局,解析器会把该函数加到windows对象的方法里面;如果作用域是函数,解析器会把该函数加到this对象的方法里面。
- 特点:在其作用域的任何地方都可以无差别调用它。
```javascript
hello(); //exer-1.html:14 Uncaught ReferenceError: hello is not defined
hello();//hello11
function hello(){
console.log("hello11");
}
arguments
- arguments 是一个对应于传递给函数的参数的类数组对象。
- 除箭头函数以外的函数都可以调用这个局部变量。
arguments 是一个对象,不是一个 Array 。它类似于Array ,但除了length属性和索引元素之外没有任何Array 属性。
使用
function func(a) {
arguments[0] = 99; // 更新了arguments[0] 同样更新了a
console.log(a);
}
func(10); // 99
把arguments转换为array ```javascript // 由于arguments不是 Array,所以无法使用 Array 的方法,所以通过这种方法转换为数组 var args = [].slice.call(arguments); // 方式一 var args = Array.prototype.slice.call(arguments); // 方式二
// 下面是 es6 提供的语法 let args = Array.from(arguments) // 方式一 let args = […arguments]; // 方式二
<a name="HgwlQ"></a>
#### js自带函数
所有的函数都要通过一个对象去调用,这也体现了js中的...一切皆对象<br />**宿主对象的方法**
1. 向页面内输出内容:
`document.write("aaa");`
2. 打印自己定义的警告信息
`console.warn('自己打印的警告信息');`
3. 打印自己定义的错误信息
`console.error('自己打印的错误信息');`
4. 查看数据类型
`console.log(typeof str);`<br />**内建对象的方法**
1. arr.forEach
1. 作用:遍历数组
1. 参数:一个回调函数,function(val,index,arr){},可以在函数里随意使用。
1. 返回值:没有返回值
```javascript
var arr = ["周一", "周二", "周三"];
arr.forEach(function(val,index,arr){
console.log("我是第" + index +"个");
console.log("我是" + val);
})
arr.sort
- 作用:对数组进行排序
- 参数:默认升序,也可以自定义一个回调函数
- 返回值:会返回一个排序好的数组,不过sort本身就会改变原数组
var arr = [4, 2, 3, 11];
arr.sort(function(a,b){
//升序:(默认的排序并非标准升序,由于是unicode一位一位的排序,11会排在最前面)
//return a - b;
//降序:
return b - a;
})
arr.splice ```javascript //0、当以该函数为属性值时: var obj = { splice : Array.prototype.splice }
/*1、删除:arr.splice(index, num); 作用:删除 参数:从index号位起删除num个数字。 返回值:被删除的部分以数组形式返回。(删除会直接作用在原数组上)
*/ var arr = [1, 2, 3 ,4]; var brr = arr.splice(index, num); console.log(arr); //[3, 4] console.log(brr); //[1, 2]
/2、添加:arr.splice(index, 0, values); 作用:添加 参数:从index号位起添加任意数量的项 返回值:空数组(添加会直接作用在原数组上) / var arr = [1, 4, 5]; var brr = arr.splice(1, 0, 2, 3); console.log(arr);//[1, 2, 3, 4, 5] console.log(brr);//[]
/3、替换:arr.splice(index, num, values); 作用:替换 参数:从index号位起先删除num项,再添加任意数量的项 返回值:被删除的部分以数组形式返回 / var arr = [1, 4, 5]; var brr = arr.splice(1, 2, 2, 3); console.log(arr);//[1, 2, 3] console.log(brr);//[4, 5]
4. arr.forEach
1. 作用:遍历数组
1. 参数:一个回调函数,function(val,index,arr){},可以在函数里随意使用。
1. 返回值:没有返回值
```javascript
var arr = ["周一", "周二", "周三"];
arr.forEach(function(val,index,arr){
console.log("我是第" + index +"个");
console.log("我是" + val);
})
arr.sort
- 作用:对数组进行排序
- 参数:默认升序,也可以自定义一个回调函数
- 返回值:会返回一个排序好的数组,不过sort本身就会改变原数组
var arr = [4, 2, 3, 11];
arr.sort(function(a,b){
//升序:(默认的排序并非标准升序,由于是unicode一位一位的排序,11会排在最前面)
//return a - b;
//降序:
return b - a;
})
Number.toFiexed(3)
- 作用:四舍五入到几位小数
- 参数:四舍五入到几位
- 返回值:新的数
var num = 1.1314;
num = num.toFixed(3);
obj.hasOwnProperty(prop)
- 作用:检查该对象是否拥有这个属性或方法
- 参数:需要判断的属性名、方法名
- 返回值:true / false
obj1.hasOwnProperty(a);
Object.keys(obj)
- 作用:获得不包括继承属性的属性名和方法名
- 参数:对象
- 返回值:以数组的形式返回对应属性名、方法名
var arr = Object.keys(chicken);
保留
- 保留
- 保留
- 保留
- 保留
- 保留
- 保留
- 保留
- 保留
- 保留
array
创建方法
//1、用字面量的方式来创建数组
var arr3= [];
var arr1 = [89, 99, 79];
var arr2 = [
{name:'peter', score:99},
{name:'tom', score:99},
{name:'tiger', score:99},
]
var arr3 = [[1, 2, 3], [4, 5, 6]];
arr3[2] = [7, 8, 9];
console.log(arr3[0][2]);
//2、用array构造函数来创建数组
var arr4 = new Array();
var arr5 = new Array(6);//指定长度
var arr6 = new Array(1, 2, 3);//指定元素
var arr7 = new Array[1];//使用中括号时,无论数字个数,都是指定元素
添加、删除元素
var arr4 = new Array();
arr4[1] = "123";//添加元素的方式,简单粗暴
console.log(arr3[0]);//undefined
//向数组的最后一个位置添加元素
arr4[arr4.length]=13;
//多余的部分会被删除,不过一般不删
arr4.length = 1;
索引
- 数组的索引是从0开始的
- 索引:值,类似于:属性名:属性值
- 数组只能用索引进行访问
访问的数组没有该索引位置 / 该索引没有定义值,会返回undefined
类数组:arguments
本质
对象
(function a(){
console.log(arguments);
}(1, 2, 3));
成立的条件
数组内容的属性名要为索引(数字)
- 必须要有length属性
- 最好加上push
运行结果:var obj = {
0:'a',
1:'b',
2:'c',
length: 3,
push:Array.prototype.push,
//加上splice,就和数组形式一样,可以像数组一样进行调用
splice:Array.prototype.splice
}
obj.push('d');
数据类型的存储
基本数据类型
- 栈空间存储
- 进行比较时,比较值(实质是比较栈空间中的值)
引用数据类型
- 堆空间(对象名还是存在栈空间的)
- 进行比较时,比较地址(实质是比较栈空间中的值)
js类型转换
js强制类型转换
js隐式类型转换
二元运算符的隐式类型转换
- 在算术运算中,其它类型的变量都会(隐式)转换成number类型的变量。
除了加号运算符中,如果存在String类型的变量,加号会变成连接符。
var str = "abc";
var num1 = 123;
var str2 = str + num1;
console.log(str2);
console.log(typeof str2);//--->string
做其它运算时可能会得到结果NaN:not a number
NaN也是number类型的
因为隐式转化无法转成number的时候就会转成NaN,而任何数字和NaN做运算都会得到NaN ```javascript var str = “abc”; var num1 = 123; var num2 = str - num1; //var num2 = str * num1; //var num2 = str / num1; //var num2 = str % num1; console.log(num2);//——>NaN console.log(typeof num2);//——>number
var str = “123”; var num1 = 123; var num2 = str - num1; console.log(num2);//—->0 console.log(typeof num2); //——>number
- null会转化成0(true、false分别是1、0)
```javascript
var num1 = 123;
var num2 = null;
var num3 = num1 * num2;
console.log(num3);//0
console.log(typeof num3);//number
一元运算符的隐式类型转换
- 对于一元运算符,‘+’、‘-’可以使别的数据类型转换为number ```javascript var num = +’12’; console.log(num);//12 console.log(typeof num);//number
var num1 = +’1’ + 2; console.log(num1);//3 console.log(typeof num1);//number
var num2 = -‘1’ + 2; console.log(num2);//1 console.log(typeof num2);//number
<a name="IXKBn"></a>
# js运算符
<a name="dRgeg"></a>
## 比较运算符-相等运算符
---
- 相等运算
- 如果类型不同,会先将类型转换成一样再比较,比如 false==0就成立
- string和number做比较,会转换成number
- <br />
- 全等运算(===(全等)和!==)
- 类型和值都一样才成立,比如 false===0就不成立
<a name="aXiLY"></a>
## 字符串的比较
---
- 字符串在比较的时候,是一位一位的比较字符的unicode编码,比如str1='abc'和str2='adc'比较,先比较两个'a'的unicode编码,发现一样,然后再比较第二位,以此类推
<a name="Ayxv5"></a>
## js运算符的优先级
---
拿捏不清楚就用括号
| **优先级** | **运算符** | **说明** | **结合性** |
| --- | --- | --- | --- |
| 1 | []、.、() | 字段访问、数组索引、函数调用和表达式分组 | 从左向右 |
| 2 | ++ -- -~!delete new typeof void | 一元运算符、返回数据类型、对象创建、未定<br />义的值 | 从右向左 |
| 3 | *、/、% | 相乘、相除、求余数 | 从左向右 |
| 4 | +、- | 相加、相减、字符串串联 | 从左向右 |
| 5 | <<、>>、>>> | 左位移、右位移、无符号右移 | 从左向右 |
| 6 | <、<=、>、>=、instanceof | 小于、小于或等于、大于、大于或等于、是否<br />为特定类的实例 | 从左向右 |
| 7 | ==、!=、===、!== | 相等、不相等、全等,不全等 | 从左向右 |
| 8 | & | 按位“与” | 从左向右 |
| 9 | ^ | 按位“异或” | 从左向右 |
| 10 | | | 按位“或” | 从左向右 |
| 11 | && | 短路与(逻辑“与”) | 从左向右 |
| 12 | || | 短路或(逻辑“或”) | 从左向右 |
| 13 | ?: | 条件运算符 | 从右向左 |
| 14 | =、+=、-=、*=、/=、%=、&=、|=、^=、<、<=、>、>=、>>= | 混合赋值运算符 | 从右向左 |
| 15 | , | 多个计算 | 按优先级计算,然后从右向左 |
<a name="vyZ7h"></a>
# 选择结构
<a name="nLWHb"></a>
## switch选择结构
```javascript
var month = 3;
switch(month){
case 1:
console.log("1月");
break;
case 2:
console.log("2月");
break;
default:
console.log("咩有定义这种情况");
break;
}
异步
实现方式
使用可以调用回调函数的函数
- setTimeout
```javascript
//简单一点
var func = function(callback){
setTimeout(function(){
},1000) } var a = function(){ console.log(“我是回调函数”); } func(a);callback();
//复杂一点 function async_fun(param, callback){//把function传进来准备调用 setTimeout(()=>{ callback(‘异步操作:’ + param);//这里才真的调用 }, 1000); }
async_fun(1, function(a){ console.log(a); async_fun(2, function(a){ console.log(a); async_fun(3, function(a){ console.log(a); }) }) })
<a name="dHKR7"></a>
### 事件监听
- 传统事件监听
```javascript
//注意:对于传统事件监听,当同一个对象绑定多个相同事件时,只有最后一次生效
//传播模式只能是冒泡,即由里到外
window.onload=function(){
alert("页面加载完毕");
}
document.getElementById("btn").onclick=function(){
alert("按钮被点击");
}
document.onmousemove=function(){
console.log("鼠标在移动");
}
- addEventListener() ```javascript //注意:对于addEventListener,可以给同一个对象绑定多个相同事件,按代码顺序执行 //一般不用,因为兼容性不好, Internet Explorer 8 及更早IE版本不支持 addEventListener() 方法,Opera 7.0 及 Opera 更早版本也不支持。 window.addEventListener(‘load’,init,false); function init(){ alert(“页面加载成功”); } //第三个参数是传播模式——> false:又里到外冒泡 true:由外到里捕获
// 下面写法与上面等价 window.addEventListener(‘load’,function(){ alert(“页面加载成功”); },false);
- ajax
- ajax是什么?
- ajax是一种不刷新页面同时更新页面的一种技术。
- ajax怎么使用?
- XMLHttpRequest构造函数
- xhr.open(请求方法,URL,是否异步请求)
- 请求方法:'get'、'post'
- xhr.onreadystatechange = () => {}
- xhr.readyState(响应状态码)
- 0:未调用open方法;
- 1:调用了open但没有调用send;
- 2:发送请求但没有收到响应;
- 3:收到了部分响应,4表示响应都接收完了。
- xhr.status(状态码)
- 200:请求被正常处
- 404:服务器无法找到对应资源
- 400:请求无效
- 401:请求需要认证
- 500:服务器内部错误
- 301:永久重定向
- 302:临时重定向
- 101:切换协议
- xhr.responseText
- xhr.send(null)
```javascript
let xhr = new XMLHttpRequest();
xhr.open('get','/eloise/visitRecord',true);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
console.log(xhr.responseText);
console.log(JSON.parse(xhr.responseText));
}
}
};
xhr.send(null);
Promise
本质
- 构造函数
- 该构造函数有一个参数,这个参数是回调函数。这个回调函数有两个参数,分别是resolve和reject,它们俩也都是回调函数。
- then
- 作用
- 可以接收到resolve和reject传递过来的数据
- then方法里面有两个参数,这两个参数都是回调函数。(一般都设置为箭头函数)
- 作用
- resolve
- 作用
- 设置异步操作执行成功之后怎么做
- 向外面传递异步操作执行成功之后的数据
- 作用
- reject
- 作用
- 设置异步操作执行成功之后怎么做
- 向外面传递异步操作执行失败之后的数据
- 作用
- catch
- 作用
- 同reject,两者二选一使用即可。
- 作用
- then
- 该构造函数有一个参数,这个参数是回调函数。这个回调函数有两个参数,分别是resolve和reject,它们俩也都是回调函数。
状态机,也就是通过设置不同的状态,来告诉用户异步操作是执行成功了还是执行失败了。
-
代码
基本版 ```javascript function test(resolve, reject){ var dp = true; if(dp === true){
resolve("dp是true");
}else{
reject("dp是false")
} } function dispose(){ return new Promise(test).then(res=>{
console.log(res);
}).catch(err => {
console.log(err);
}) }
//被注释掉的dispose和上面那种写法等效 //function dispose(){ // return new Promise(test).then(res=>{ // console.log(res); // }, err=>{ // console.log(err); // }) // }
dispose();
- 不要test的改写版
```javascript
function dispose(){
return new Promise(function(resolve, reject){
var dp = true;
if(dp === true){
resolve("dp是true");
}else{
reject("dp是false")
}
}).then(res=>{
console.log(res);
}, err=>{
console.log(err);
})
}
dispose();
- 标准版
- 可以做到更加清晰的分离 ```javascript function dispose(){ return new Promise(function(resolve, reject){ var dp = true; if(dp === true){ resolve(“dp是true”); }else{ reject(“dp是false”) } }) }
dispose().then(res=>{ console.log(res); }, err=>{ console.log(err); });
- 链式then
- 原理:then本身就会返回一个promise对象,但如果不写成return new Promise的形式就只有resolve,没有reject。
```javascript
function dispose(){
return new Promise(function(resolve, reject){
var dp = true;
if(dp === true){
resolve("dp是true");
}else{
reject("dp是false")
}
})
}
dispose().then(res=>{
console.log(res);
var dp2 = true;
return new Promise(function(resolve, reject){
if(dp2 === true){
resolve("dp2是true");
}else{
reject("dp2是false")
}
})
}, err=>{
console.log(err);
}).then(res => {
console.log(res);//我是链式then中的第二个then
});
并行执行异步任务
let p1 = new Promise((resolve, reject) =>{
resolve("成功了");
})
let p2 = new Promise((resolve, reject) =>{
resolve("success");
})
Promise.all([p1, p2]).then(res=>{
console.log("都做了");
console.log(res);
})
应用
超时处理
封装处理ajax请求
原生封装
function ajax(){
return new Promise(function(resolve, reject){
let xhr = new XMLHttpRequest();
xhr.open('get', '/eloise/visitRecord', true);
xhr.onreadystatechange = () =>{
if(xhr.readyState === 4){
if(xhr.status === 200){
console.log("here");
resolve(JSON.parse(xhr.responseText));
}else{
reject(xhr.status);
}
}
}
xhr.send(null);
})
}
ajax().then(res =>{
console.log(res);
}).catch(err =>{
console.log("失败原因:"+err);
})
axios
- 本质:一种基于Promise封装ajax的技术。 ```javascript //在react中引入axios import axios from ‘axios’; React.Component.prototype.$axios = axios;
this.$axios.get(‘/eloise/visitRecord’).then(res=>{ console.log(res.data); }).catch(function (error) { console.log(error); });
- fetch
- 本质:一个基于promise封装的一个http库,是原生js,没有封装ajax,也没有xhr
- 特点
- 原生函数, 但老版本浏览器不支持,为了兼容低版本的浏览器, 可以引入兼容库fetch.js
- fetch对应的后端只能是get
- 在react里,使用fetch不需要引入什么,直接fetch就用了
<a name="pEEy1"></a>
#### promise的好处和优点
- promise 的出现解决了传统 callback 函数回调地狱的问题,支持链式调用,但当遇到复杂的业务逻辑时,过多的调用then会显得很不美观。
- 回调地狱是什么?
- .因为javascript是单线程的,所以有些需要等待的地方,需要使用回调函数。但如果业务复杂,就需要写多层的回调嵌套,这样的代码不仅书写麻烦、可读性差,而且难以维护,这就称作回调地狱。一般处理的方式是使用promise或者async函数。
<a name="qBLxq"></a>
### async/await
<a name="uQkYW"></a>
#### 使用
略
<a name="DI2GG"></a>
#### 和promise的区别
- async,await 是基于 promise 实现的,async函数会返回一个Promise对象。它是基于 Generator 函数的语法糖,拥有内置执行器,可以使异步代码看起来像同步代码一样,不需要再写promise里的then,更加方便阅读和理解。
<a name="bezMW"></a>
#### 应用
- 单独使用
- 普通函数
```javascript
//便于理解(就写在全局作用域的,咋没看出来非法)
async function a(){
await setTimeout(b,1000);
await c();
await d();
}
function b(){
console.log("b函数");
}
function c(){
console.log("c函数");
}
function d(){
console.log("d函数");
}
a();
//实际应用:这是react里的一个函数
componentDidMount() {
var that = this;
async function getData(){
const url1 = '/eloise/visitRecord';
const url2 = '/eloise/indexChoice';
const res1 = await that.$axios.get(url1);
console.log(res1.data);
const res2 = await that.$axios.get(url2);
console.log(res2.data);
}
}
- 箭头函数 ```javascript var a = async () =>{ await b(); await c(); }
function b(){ console.log(“b函数”); }
function c(){ console.log(“c函数”); }
a();
- 立即执行函数
```javascript
(async ()=>{
await b();
await c();
}
)()
function b(){
console.log("b函数");
}
function c(){
console.log("c函数");
}
a();
用在promise链中
条件:async函数需要返回一个Promise对象,可以直接return await,因为await会返回一个promise对象
componentDidMount() {
var that = this;
async function getData(){
const url1 = '/eloise/visitRecord';
const url2 = '/eloise/indexChoice';
const res1 = await that.$axios.get(url1);
console.log(res1.data);
return await that.$axios.get(url2);
}
getData().then(res=>{
console.log(res.data);
},err=>{
console.log(err);
})
}
处理错误
通过promise中then方法的回调函数参数来处理
//url2是一个错误地址
componentDidMount() {
var that = this;
async function getData(){
const url1 = '/eloise/visitRecord';
const url2 = '/eloise/indexChoice1';
const res1 = await that.$axios.get(url1);
console.log(res1.data);
return await that.$axios.get(url2);
}
getData().then(res=>{
console.log(res.data);
},err=>{
console.log(err);
})
}
判断状态码,直接抛出错误
//虽然url2是对的,但是then方法还是进了error那里
componentDidMount() {
var that = this;
async function getData(){
const url1 = '/eloise/visitRecord1';
const url2 = '/eloise/indexChoice';
const res1 = await that.$axios.get(url1);
if(res1.status !== 200){
throw new Error(res1.status)
}
return await that.$axios.get(url2);
}
getData().then(res=>{
console.log(res.data);
},err=>{
console.log("我是err");
console.log(err);
}).catch(cat =>{
console.log("我是catch");
console.log(cat);
})
}
-
闭包
闭包的基本点
定义:定义在一个函数内部的、且 调用了 该函数 局部变量 的函数。
- 闭包产生的条件
- 函数嵌套
- 子函数要使用函数内部的变量
- 性质:
- 闭包是连接函数内外部的桥梁。可以实现在函数外部操作函数内部。
- 所以只要不return出去,外面只能干瞪眼
- 闭包对应的函数中的局部变量是常驻内存的,以此来实现缓存。
- 因此,往往需要保证外部函数只执行一次。否则对象重新创建、重新初始化,无法实现缓存(实质是无法实现变量常驻内存)。
- 可以使用立即执行函数,保证只执行一次。
- 只要保证之后操作的一直是return值(闭包函数)就没问题。
- 外部函数只执行一次,是实现缓存的基础。缓存又是实现其它应用的基础。
- 因此,往往需要保证外部函数只执行一次。否则对象重新创建、重新初始化,无法实现缓存(实质是无法实现变量常驻内存)。
- 闭包是连接函数内外部的桥梁。可以实现在函数外部操作函数内部。
- 缺点
- 由于对应变量常驻内存,可能导致内存泄漏
- 如何解决:将外部调用闭包的对象置为null ```javascript function f1(){ var a = 1; function f2(){ console.log(++a);//函数内部 + 调用局部变量—->满足闭包定义 } return f2;//return出去,实现桥梁 }
- 由于对应变量常驻内存,可能导致内存泄漏
var f3 = f1();//f1只执行一次,实现缓存 f3();//2 f3();//3
fn3 = null;//闭包fn2被回收,避免内存泄漏
<a name="oYBsw"></a>
## 闭包的应用
1. 实现缓存
1. 规则:
1. 缓存里如果没有的话,就新建对象到缓存,如果缓存里面有,就直接从缓存里面拿。
1. 外部函数只能执行一次。//所以这里是立即执行
2. 好处:节约资源,不必反复创建同一对象。
```javascript
var Cache1 = (function(){
var cache = {};//变量cache会常驻内存
return {//也可以先定义一个有名字的局部方法,再return出来
getObj:function(name){
if(name in cache){
return cache[name];
}
var temp = new Object(name);
cache[name] = temp;
return temp;
}
}
})();
console.log(Cache1);//return对象
console.log(Cache1.getObj('乔碧萝'));
实现封装过程,私有化变量
- 规则
- 封装对象中的变量不能直接访问,只能通过闭包来访问。
- 好处:避免非法访问
var person = (function(){
var name = "还没有名字";//外部访问不到name值,打印person.name是undefined,但可以通过函数看到
return{
getName:function(){
return name;
},
setName:function(value){
name = value;
}
}
}());
person.setName("小张");
console.log(person.getName());//小张
//如果person执行两次,那么setName就不会生效
- 规则
自定义模块
- 规则
- 好处:每个模块都可以定义相同名字的变量,多模块协作时也不会引起冲突。
- 本质:可以使得同名属性最终加载到不同的对象上
```javascript //exer-2.js (function(){ var m = “我是模块2中定义的变量m”; function a(){ return m + “:a函数”; }; function b(){ return m + “:b函数”; }; window.module2 = { a:a, b:b } })();//exer-1.js
function module1(){
var m = "我是模块1中定义的变量m";
function a(){
return m + ":a函数";
};
function b(){
return m + ":b函数";
};
return{
a:a,
b:b
}
}
//标准来讲,应该这么写: //因为js里面涉及压缩的问题,window可能会被压成w,上面的写法就不行了 (function(window){ var m = “我是模块2中定义的变量m”; function a(){ return m + “:a函数”; }; function b(){ return m + “:b函数”; }; window.module2 = { a:a, b:b } })(window);
```javascript
//1.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>js简单实例</title>
</head>
<body>
</body>
<script src="exer-1.js"></script>
<script src="exer-2.js"></script>
<script>
//模块1
var accept = module1();
console.log(accept.a());
//模块2
console.log(module2.a());
</script>
</html>
this
- this是什么?
- this代表的是函数(方法)所在的那个对象。【this是一个指示代词,是这、这个的意思,】
- this就是代码执行时当前的context object
this的指向问题
纯粹的函数
- this指向window
function test(){
console.log(this)
}
test(); //window
- this指向window
对象中的方法
- this指这个上级对象 ```javascript //形式1 function test(){ console.log(this); } var obj = {}; obj.fn = test; obj.fn();//obj
//形式2 var obj = { fn:function(){ console.log(this); } } obj.fn();//obj
- 对象方法中的方法
- 指向window
```javascript
var obj = {
f1:function(){
var f2 = function(){
console.log(this);//window
}
f2();
}
}
obj.f1();
- 作为构造函数调用
- this指向新对象 ```javascript function Test(){ console.log(this); } var obj = new Test();//Test()
Test.prototype.sayHello = function(){ console.log(this); } obj.sayHello();//Test()
- 箭头函数
- 箭头函数内部所有的this指向首层父级作用域的this
- 自执行函数
- 指向window
```javascript
var obj = {
f1:function(){
console.log(this);
},
f2:(function (){
console.log(this);//window
})()
}
obj.f1();//obj
- 回调函数
- 被箭头函数包裹
- 首层的父级作用域
- 不被箭头函数包裹
- window
- 为什么在函数里可以直接打印this?
- window
- 被箭头函数包裹
因为浏览器(解析器)在调用函数的时候,会向函数的内部传递一个隐含的参数,这个参数就是this
function fun1(){
console.log(this);
}
fun1();//window
window.fun1();//window
var zhubajie = {
name:"猪八戒",
f:fun1
}
zhubajie.f();//object->zhubajie
- this的作用
原型的基本点
什么是原型
- 原型是function对象的一个object属性。
- 默认的 “prototype” 是只有一个属性 constructor,属性 constructor 指向构造函数自身。
- prototype.constructor也可以改变
- 它定义了以该函数作构造函数时,创建的对象的公共组先,这些对象直接继承原型的属性和方法。 ```javascript //1、为构造函数的属性prototype赋值 Person.prototype = { skeleton:206, chara:”lazy” } //或者:Person.prototype = new DaXingXing(); function Person(){
} var p1 = new Person(); var p2 = new Person();
//2、法二:使用proto var animal = { eat:”动物都会吃饭”, };
var chicken = { crow:”鸡会打鸣”, proto:animal }; //或者 chicken.proto = animal
<a name="FvGDl"></a>
### 原型的作用
- 数据共享,节省空间
- 继承:增加了复用性、可读性、维护性
<a name="uKPec"></a>
### 原型的特点
- 原型只用于读取,不用于写入、删除。
- 引用值除外,可以给引用值加属性
- 写入非引用值外的属性,相当于给自己加属性
- 先在非继承属性和方法中找,找不到再向原型链找。
- 查看A对象的原型链上有没有B的原型(b.prototype)
```javascript
A instanceof B //返回true or false
var animal = {
eat:"动物都会吃饭",
sleep:function(){
console.log("动物都会睡觉");
}
};
var chicken = {
crow:"鸡会打鸣",
__proto__:animal
};
chicken.sleep();//从原型读取:动物都会睡觉
chicken.sleep = function(){ //直接在对象上写入
console.log("鸡会站着睡觉");
}
chicken.sleep();//鸡会站着睡觉
delete chicken.sleep; //直接在对象上删除
chicken.sleep();//动物都会睡觉
原型的限制
- 引用不能形成闭环。如果我们试图在一个闭环中分配 proto,JavaScript 会抛出错误。//?没错,会报undefined
proto 的值可以是对象,也可以是 null。而其他的类型都会被忽略(访问显示undefined)
proto是什么?
proto是对象所独有的(函数也是对象)
- p1.proto === Person.prototype
- proto (Google) 是 [[Prototype]] (ES6) 的 getter/setter
- proto属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的proto属性所指向的那个对象(父对象)里找,一直找,直到proto属性的终点null,再往上找就相当于在null上取值,会报错。通过proto属性将对象连接起来的这条链路即我们所谓的原型链。
现代编程语言建议我们应该使用ES5引入的函数 Object.getPrototypeOf/Object.setPrototypeOf 来取代 proto 去 get/set 原型。因为有些浏览器并不支持这个proto属性,所以并不是完全兼容的。
constructor是什么
单从constructor这个属性来讲,只有prototype对象才有
- 每个对象(函数也是对象)都可以找到其对应的constructor,因为创建对象的前提是需要有constructor,而这个constructor可能是对象自己本身显式定义的或者通过proto在原型链中找到的
- constructor属性的含义就是指向该对象的构造函数
所有函数(此时看成对象了)最终的构造函数都指向Function。
for…in循环
for…in循环会迭代①包括继承的在内的②可枚举的所有属性名和方法名
- 结合hasOwnProperty可以只循环自己的属性
- 疑问:hasOwnProperty这个属性是哪里来的?
- 该方法是 Object.prototype.hasOwnProperty 提供的。换句话说,它是继承的。
- 疑问:那为什么 hasOwnProperty 没有像 eats 和 jumps 那样出现在 for..in 循环中?
- 它是不可枚举的。就像 Object的其他属性,hasOwnProperty 有 enumerable:false 标志。
- 疑问:hasOwnProperty这个属性是哪里来的?
- 数组也能for…in ```javascript //迭代包括继承在内的属性 for(var i in chicken){ console.log(chicken[i]); }
//结合hasOwnProperty可以只循环自己的属性 for(var i in chicken){ var isOwn = chicken.hasOwnProperty(i); if(isOwn){ console.log(chicken[i]); } }
<a name="VS3ey"></a>
## 共享原型(的继承)
- 因为是引用,所以原型不能随意改变,改一个会把大家的都改了
```javascript
function(Target, Origin){
Target.prototype = Origin.prototype;
}
圣杯模式(的继承)
- 增加一个F中间层
- 更改Son.prototype不会改变Father.prototype ```javascript fucntion F(){}; F.prototype = Father.prototype; Son.prototype = new F();
function(Target, Origin){ //增加一个中间层 function F(){}; //先定义中间层的原型 F.prototype = Origin.prototype; //再定义Target的原型 Target.prototype = new F();
//考虑constructor要指向自己的构造函数 Target.prototype.constructor = Target; //如果有一天想知道真的继承自谁,可以通过这种方法去调用 Target.prototype.uber = Origin.prototype; }
- 创建原型
```javascript
//1、自定义原型 法一:使用__proto__
var animal = {
eat:"动物都会吃饭",
};
var chicken = {
crow:"鸡会打鸣",
__proto__:animal
};
//或者 chicken.__proto__ = animal
console.log(chicken.eat);//动物都会吃饭
var egg = {
circle:"鸡蛋是圆的",
__proto__:chicken
}
//1、查看原型
var xiaowang = new Object();
console.log(Object.getPrototypeOf(xiaowang));
call/apply、bind
基本概念
作用
改变this指向
//1、call用法1
test()<--->test.call();
//2、call用法2:如果有参数,第一位得传this的指向
var obj = {}
function Person(name, age){
this.name = name;
this.age = age;
}
Person.call(obj, 'ANNA', 19);
//obj = {name:'ANNA', age:19};
//1、apply用法:
//call需要把实参按照形参的个数传进去
//apply需要传一个arguments
Person.apply(obj, ['ANNA', 19]);
//call和apply返回一个函数调用,但bind只会返回一个函数
//1、bind用法1
Person.bind(obj, 'ANNA', 19)();
//2、bind用法2
Person.bind(obj)('ANNA', 19);
应用
(借用构造函数的)继承
- 不能继承借用构造函数的原型
每次构造函数都要走一个函数(从运行上没省)
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, grade){
Person.call(this, name, age, sex);
this.grade = grade;
}
var student = new Student();
区别是数组还是对象
综合使用Object.prototype.toString方法和call方法 ```javascript Object.prototype.toString.call([]);//[object Array] 第一个单词小写,第二个单词大写 Object.prototype.toString.call({});//[object Object]
//真实应用 var toStr = Object.prototype.toString; if(toStr.call(target) == “[object Array]”){ console.log(“这是数组”); }else{ console.log(“这是对象”); }
<a name="krGia"></a>
# try..catch
<a name="vvxQ3"></a>
## 作用
- try用于容错,不会执行错误后的try里面的代码,不影响try..catch后面的代码,不会向控制台抛出异常
- catch用于捕获异常,只有在try代码块中出现错误代码时才会执行catch里的代码
```javascript
try{
console.log('a');
console.log(b);//运行到这里停止
console.log('c');
}catch(e){
console.log(e);//ReferenceError: b is not defined
// at exer-1.html:13
console.log(e.message);//b is not defined
console.log(e.name);//ReferenceError
}
console.log('d');
错误类型
- EvalError:eval()的使用与定义不一致
- RangeError():数值越界
- ReferenceError:非法或不能识别的引用数值
- SynaxError:语法解析错误
- TypeError:操作数类型错误
- URLError:URL处理函数使用不当
dom
- 全称:Document Object Model,即文档对象模型。
- 概念:DOM 是 W3C(万维网联盟)的标准,是关于如何获取、修改、添加或删除 HTML 元素的标准。