[toc]
- 省略了部分基础内容, 来自腾讯课堂渡一教育
- 章节
- 变量, 值类型, 运算符
- 声明变量
var a=2, b=3, c;
- 分类
- 原始值: Number, Boolean, String, undefined, null.
存储在栈中.
var a = 10; b = a; a = 20; console.log(b) //输出10
- 引用值: Array, Object, function.. date ReExp.
存储在堆中
- 原始值: Number, Boolean, String, undefined, null.
``` var a = [1,2]; b = a; a.push(3); console.log(b); //输出[1,2,3] a = [1,3]; console.log(b); //输出[1,2,3] ```
- 运算符
- '+'
- 数学运算和字符拼接.
- 任何数据类型和字符串相加为字符串
- '* / - %'
1/0 //infinite -1/0//-infinite 0/0//NaN
- 比较运算符, 结果布尔值
- 字符串比较ascii码
NaN == NaN //false
- &&和||:返回结果根据前后的表达式是否为真, 返回表达式的值
表达式结果 undefined, null, NaN, '', 0 , false转换为false
2 && 3+1//相当于if语句
- '+'
- typeof , 类型转换
-
typeof
- Number, String, undefined, object, function
- 返回的值是字符串
- 未声明的变量返回的是undefined, 不会报错
-
显示类型转换
- Number()
``` Number(true)//1 Number(null)//0 Number(undefined)//NaN ```
- parseInt()
parseInt(true)//NaN
-
- 变量, 值类型, 运算符
parseInt(12.9)//12
parseInt(10, 16)//16, 以第二个参数为基底转换成10进制
```
- parseFloat()
- String()
- Boolean()
- toString()
- undefined, null不能用toString()
- toString(<radix>)把10进制数字转化为目标进制
//2进制转换为16进制
var num = 101010;
var test = parseInt(num, 2);
test.toString(16);
2.3.4 隐式类型转换
- isNaN()
调用Number() - ++/-- +/-(一元正负)
调用Number() - '+'
如果有字符串, 调用String() - */%-
调用Number() - && || !
- < > <= >=
有数字调用Number() - == !=
有数字会调用Number()
var str = false + 1; //str是1
var demo = false == 1;//false
if(typeof(a)&&-true +(+undefined) +''){
}//undefined&&'NaN', 为true
if(11 + '11' * 2 == 33){}//if为true
!!' ' + !!'' - !!false //为1
typeof(null)//object
2.4 函数, 初始作用域
2.4.1 函数
- 定义
funtion test(){};
//匿名函数表达式
var test = function(){};
test.name //test
(window.foo || (window.foo = 'bar'));//(window.foo = 'bar')括号有优先级, 先赋值
- 参数
形参和实参个数可以不一致
function test(){
console.log(arguments.length)//实参长度, arguments实参列表, 映射关系, 其中元素和实参不是同一对象
console.log(test.length)//形参长度
}
2.5 预编译
2.5.1 函数声明, 变量声明提升
2.5.2 暗示性声明变量(imply global):会将变量变为全局对象所有
function test(){
var a=b=123//暗示性声明, 先将123赋值给b, b为全局所有
}
2.5.3 预编译步骤(在函数执行前一刻)
- 创建一个AO对象, 执行期上下文
- 找形参和变量声明, 作为AO对象的属性名, 值为undefined
- 将实参和形参相统一
- 找函数体的函数声明, 值赋予函数体
//试题1
function fn(a){
console.log(a);//function a(){}
var a =123;
console.log(a);//123
function a(){};
console.log(a);//123
var b = function(){};
console.log(b);//function(){}
function d(){}
}
fn(1)
// 1. AO
/*2. AO{
a: function a(){},
b: undefined,
d: function(){}
}未执行前的AO*/
//试题2
a = 100;
funtion demo(e){
funciton e(){}
arguments[0] = 2;
console.log(e);//2
if(a) {
var b = 123;
function c(){}//现在语法不允许if中声明函数
}
var c;
a = 10;
var a;
console.log(b);//undefined
f = 123;
console.log(c);//function c(){}, 实际为undefined
console.log(a);//10
}
var a;
demo(1);
console.log(a);//100
console.log(f);//123
/*GO{
a:undefind,
demo: function demo(e){},
f: undefind,
}
AO{
e:function e(){},
b: undefind,
c: function c(){},
a: undefind
}demo未执行前*/
//试题
var x = 1, y = z = 0;
function add (n) {
return n = n +1;
}
y = add(x);
function add (n) {
return n = n + 3;
}
z = add(x);
//x y z 考察预编译环节
//1 4 4
2.6
2.7 作用域 作用域链 立即执行函数
2.7.1 [[scope]]: 每个js函数都是一个对象, 对象中有些属性仅供引擎存取.[[scope]]就是其中之一, 里面有运行期上下文集合
2.7.2 运行期上下文: 当函数执行时, 会创建一个执行期上下文的内部对象. 一个执行期上下文定义了一个函数执行时的环境, 函数每次执行时对应的上下文都是独一无二的.
2.7.3 作用域链: [[scope]]里的集合成链式连接, 我们称其为作用域链
2.7.4 查找变量: 从作用域链的顶端依次向下查找
//示例函数和作用域链
function a () {
function b () {}
var a = 123;
b();
}
var glob = 100;
a();
2.7.5 闭包
2.7.5.1 示例
function a () {
function b(){
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();
- 定义: 当内部函数被保存到外部时, 将会生成闭包.闭包会导致原有作用域不释放, 造成内存泄漏(内存占用不释放, 看起来能用的内存变少)
- 作用:实现公有变量;可以做缓存(存储结构);可以实现封装, 属性私有化;模块化开发, 防止污染全局变量
//公有变量示例
function add () {
var count = 0;
function b () {
count ++;
console.log(count)
}
return b;
}
2.7.6 立即执行函数
- 定义: 此类函数没有生命, 在一次执行后立即释放, 适合做初始化工作.
- 形式
(function () {} ())
- 来自: (只有)表达式可以被执行符号执行
+ function test(){
console.log('ass')
}()
//或者
var test = function test(){}()
//如果去掉+, 则报错. 因为运算符将其变成表达式
//()同样也是一个运算符
//下面有一个变形
function (a, b){console.log(a+b)}(1,2);
//不会报错, 上面一行会被视作两行, 不加实参报语法错误
- 运用
function test(){
arr = [];
for(var i=1;i<10;i++){
arr[i] = function (){
console.log(i)
}
};
return arr;
}
var myarr = test()
for(var j=1;j<10;j++){
myarr[j]()
}
//输出9个10, 这是个闭包问题, function定义的时候没有马上执行, 当循环完毕后, test中的AO中i是10
//上面的解决办法
funtion test(){
arr = [];
for (var i=1; i<10; i++){
function (j){
arr[j] = function(){
console.log(i)
}(i);//添加个立即执行函数, 将i转为j变量. 相当于加个中间层的AO, 这个AO在函数test执行的时候同时执行, 并保存下i
};
};
return arr;
}
//li加一个点击事件, 打印相应的索引
function test () {
var liCollection = document.getElementByTagName('li');
for( var i = 0; i < liColletion.length; i++){
(function (j) {
liColletion[j].onclick = function () {
console.log(j);
}//外部函数, 注意闭包
}(i))
}
}
var f = (
function f () {
return '1';
},
function g () {
return 2;
}
)();
typeof f;//number
var x = 1;
if (function test(){}){
x += typeof test
}
console.log(x)//输出1undefined
//if语句的()使function test(){}变成一个表达式,返回一个function test()结果为真
//定义一个函数, 直接用函数名是undefined, 需要将其赋值到一个变量
2.8 对象 包装类
2.8.1 对象:是一种基础的变量类型
2.8.2 增删改查:
delete obj.name//不存在的属性返回为true
obj.name//访问不存在的属性为undefined
2.8.3 创建方法:
//1
var obj = {}//对象字面量/对象直接量
//2.构造函数
//系统自带的构造函数 Object Array Number
var obj = new Object();//相当于var obj = {};
//自定义
2.8.4 构造函数内部原理(与函数的区别)
- 在函数体最前面生成一个this对象 var this = {}
- 执行this.xxx = xxx
- 在函数的最后隐式的return this
如果显示的返回原始值, 则会被系统忽略该句
2.8.5 包装类
var num = new Number(123);
//num 是一个数字对象, 参与运算后变成数字
//可以添加属性
var str = new String('ab')
原始值不能有属性
var num = 4;
num.len = 3;
//new Number(4).len = 3 delete
//new Number(4).len undefinde delete
console.log(num.len);//undefinde
var str = 'abcd';
str.length = 2;
console.log(str.length)//4, 系统自带, 似乎不能改
2.9 原型链 call/apply
2.9.1 原型
- 定义: 原型是function对象的一个属性, 他定义构造函数制造出的对象的公共祖先. 通过该构造函数产生的对象, 可以继承该原型的属性和方法.
- 对象隐式属性:
__proto__
-对象查看构造函数: constructor
Person.prototype.name = 'sunny';
function Person (){
//var this = {__proto__ : Person.prototype}
}
var person = new Person();
Person.prototype = {
name : 'cherry'
}
console.log(person.name)//输出sunny
//同下
/* var obj = {name: 'a'};
var obj1 = obj;
obj = {name:'b'}
*/
- 创建对象另一种方法:
var obj = Object.create(<原型>)
var obj = Object.create(null)//不继承自Object.prototype
//手动加上__proto__系统不会承认
123.toString();//.会被识别为小数点
0.14 * 100//计算精度位小数点前后16位
2.9.2 call/apply
test() --> test.call()
call和apply第一个参数(对象)改变this的指向, 不同是的两者的传参不一样.apply传递参数的是数组
2.10 继承模式 命名空间 对象枚举
2.10.1 继承模式之一
function inherit(Target, Origin){
Function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;//不加的话会指向Origin, 构造对象的constructor继承原型
Target.prototype.uber = Origin.prototype
}
2.10.2 命名空间:变量放在对象里;闭包
2.10.3 属性遍历
var obj = {
__proto__:{
lastName:'ha'
},
prop: 123,//obj.name --> obj['name']
}
for(var prop in obj){
console.log(prop)
console.log(obj[prop])//写成obj.prop错误
//会打印'ha', 系统的__proto__不会打印, 自己设的会打印 object.prototype.name = 'ha'
//如果不想打印__proto__, 用hasOwnPorperty
}
//'height' in obj比hasOwnProperty范围大
2.10.4 instanceof
A instanceof B //A的原型链是否有B的原型
2.11 this
2.11.1 this分析
- 函数预编译过程中this --> windnow
- 全局作用域中的this --> window
- call/apply会改变this的指向
- obj.func(), 谁调用func指向谁
var name = '111'
var a = {
name:'222',
say: function(){
console.log(this.name)
}
}
var fun = a.say
fun()//111, 变成全局环境了
a.say()//222
var b = {
name : '333',
say: function(fun){
fun()
}
}
b.say = a.say
b.say()//333, 只是方法的引用
b.say(a.say)//111
//this, 笔试真题讲解下
var foo = 123;
function print () {
this.foo = 234;
console.log(foo);
}
print();//234分清this的指向
new print();//123
// 运行test() 和new test()的结果分别是?
var a = 5;
function test () {
a = 0;
alert(a);// 0 0
alert(this.a);// 5 undefined
var a;
alert(a);//0 0
}
2.12 克隆
2.12.1 深克隆
function deepClone (origin, target) {
var target = target || {},
toStr = Object.prototype.toString,
arrStr = '[object Array]',
for (var prop in origin){
if (origin.hasOwnProperty(prop)){
if(type0f(origin[prop]) == 'object' && origin[prop] !== 'null') {
if(toStr.call(originp[prop]) == arrStr){
target[prop] = [];
} else{
target[prop] = {};
}
deepClone(origin[prop], target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
2.12.2 数组
- 索引可以越界, 没有-1位
var arr = new Array(10)//如果只有一位参数, 将被作为长度为10的空数组
- 数组方法
- 改变原数组
- push, pop, shift, unshift(-->push), sort, reverse
- splice
- 改变原数组
//push方法实现
Array.prototype.push = function(){
for(var i = 0; i < arguments.length; i++){
this[this.length] = arguments[i]//this.length是调用该方法的数组对象
}
return this.length
}
//数组方法有负一位
//负数位实现
function (pos) {
pos += pos>0?0:this.length;
}
//sort本身是比较ASCII码排序
//必须有来个形参
//返回值为负, 前面的数放在前面, 为0不动, 为正后面的数在前面
arr.sort(function (a, b){
return a-b//升序
});
- 不改变原数组
- concat, join --> split, toString, slice
2.12.3 类数组
- 属性必须是索引(数字)属性, 必须要有length
var obj = {
"0":'a',
"1":'b',
"length":2,//和属性索引具有联动性
"push":Array.prototype.push,
"splice":Array.prototype.splice//加了之后控制台会打印成数组形式[]
}
- 属性可配置和不可配置性
var num =123;
一旦经历了var的参考做, 所得出的属性是不可配置属性, delete不掉