js基础

156 阅读8分钟

值类型

  • 原始值(stack)
    • number string boolean null undefined
  • 引用值(heap)
    • object array function ...
var num = 100;
num= 200;

var a = 10;
var b = a;
a = 20;
console.log(b)

var b = a; 过程为a先取出来10拷贝一份放在b里面,不管是改a里面的10还是b里面的10,和另外一个都没有关系。栈内存和栈内存之间的赋值是拷贝关系。

var arr = [12];
var arr1 = arr;
console.log(arr1)

var arr = [1];
var arr1 = arr;
arr = [1,3];
console.log(arr1)

arr = [1,3] 会从堆内存重新申请一个房间

function deepClone(origin, target){
    var target = target || {};
        toStr = Object.prototype.toString;
        arrStr = "[object, Array]";
    for(var prop in origin){
        if(origin.hasOwnProperty(prop)){
           if((origin[prop]) !== "null" && typeof(origin[prop]) == 'object'){
               if(toStr.call(origin[prop]) == arrStr) {
                   target[prop] = [];
               } else {
                target[prop] = {};
               }
               deepClone(origin[prop],target[prop])
           } else {
               target[prop] = origin [prop]
           }
        }
    }
    return target;
}

var a = {
    name: 123,
    wife: {
        name: 'ceshi'
    }
};
var b = {};
deepClone(a, b)

运算符

  • 比较运算符
    • '>' '<' '==' '>=' '<=' '!='
    • 比较结果为boolean值
  • 逻辑运算符
    • '&&' 先看第一个表达式转换为布尔值的结果 如果为真,那么它会看第二个表达式转换为布尔值的结果, 如果为假,直接返回第一个表达式转换为布尔值的结果。(全真为真,一假为假)
    • '||' 先看第一个表达式转换为布尔值的结果 如果为真,那么它会看第一个表达式转换为布尔值的结果, 如果为假,返回第二个表达式转换为布尔值的结果。(一真为真,全假为假)
    • '!' 转换为布尔值 然后取反
  • 被认定为false的值
    • undefined null NaN '' 0 false

循环语句

  • for循环
  • while (满足条件执行)
  • do while(无论条件是否满足,首先要执行一次)
  • switch case
var n = 'a';
switch(n) {
  case "a":
    console.log('a');
    break;
  case "b":
    console.log('b');
    break;
  case "c":
    console.log('c');
    break;
}
  • break 终止循环 一定要放在循环里面
  • continue(终止本次循环,执行下一次循环)
// 如果满足整除7,则不打印。
for(var i = 0; i < 100; i++){
	if(i % 7 === 0){
    	continue;
    }
    console.log(i)
}

typeof 类型转换

  • number
  • string
  • boolean
  • object
  • undefined
  • function
var num = NaN;
typeof(num)  "number"

var num = [];
typeof(num)  "object"

var num = null;
typeof(num)  "object"

var num = undefined;
typeof(num)  "undefined"

var num = function(){};
typeof(num)  "function"

显示类型转换

  • Number()
  • parseInt(string, radix)
  • parseFloat()
  • toString()
  • Boolean()
  • String()
var num = Number('123'); 123
var num = Number(false);  0
var num = Number(undefined) NaN
var num = Number('abc') NaN
var num = Number('123abc') NaN
var num = parseInt(true); NaN
var num = parseInt(false);  NaN
var num = parseInt(undefined) NaN
var num = parseInt('abc') NaN
var num = parseInt('123.9') 123
var num = parseInt('123abc') 123
var num = undefined;
num.toString();    // 报错

var num = null;
num.toString();    // 报错

隐式类型转换

  • isNaN()
  • ++ / -- (加加 或 减减) +/-(正负)
  • +(加号)
  • -(减号)*(乘号)/(除号)%(摩尔)
  • && || !
  • < = >= <=

  • == !=
isNaN('abc')
// 实际过程为:
Number('abc') 然后 和 NaN 对比

isNaN(null)   
Number('null') 0 ===> NaN false

isNaN(undefined)   
Number('undefined') NaN ===> true
// 正负号隐式类型转换调用的是Number()
var a = +'abc'
console.log(typeof(a)) number
console.log(a) NaN

 加号隐式类型转换调用的是String();
 当加号两侧有一个是字符串类型,会调用String(),全部转换为字符串类型。
 -(减号)*(乘号)/(除号)%(摩尔) 隐式类型转换调用的是Number()
NaN == NaN false
undefined == null true

注意:返回值为字符串
typeof(a) 'undefined'
typeof(typeof(a)) 'string'

函数

定义函数

  • 函数声明
  • 函数表达式
// 函数声明
function theFirstName(){
}
// 命名函数表达式
var test = function abc()  {
  document.write('a');
}
// 匿名函数表达式 (简称函数表达式)
var demo = function () {
  document.write('b');
}

arguments

在每一个函数里面都有一个arguments叫做实参列表.为数组。

function sum(a, b) {
  //arguments [1, 2]
  a = 2;
  console.log(arguments[0]) 2 
}
sum(1,2);

function sum(a, b) {
  //arguments [1, 2]
  a = 2;
  arguments[0] = 3;
  console.log(a); 3
}
sum(1,2);

函数内部有一条映射规则,形参改变,arguments也会跟着改变。arguments改变,形参也会改变。

function sum(a, b) {
  a = 2;
  console.log(arguments[0]) 2
}
sum(1);

// 下面这种情况不会发生映射
function sum(a, b) {
  b = 2;
  console.log(arguments[1]) undefined
}
sum(1);

练习 递归实现n的阶乘

function mul(n) {
  if(n === 1 || n === 0){
      return 1;
  }
  return n  * mul(n - 1)
}
mul(5);

作用域

  • 定义: 变量和函数生效的区域。

预编译

js运行过程

  • 语法分析
  • 预编译
  • 解释执行(解释一行,执行一行)
// 函数声明 整体提升
test();
function test() {
  console.log('a');
}
// 变量 声明提升
console.log(a); undefined
var a = 123;

预编译前奏

  1. imply global 暗示全局变量:即任何变量,如果未经声明就赋值,此变量就为全局对象所有。
eg: a = 123;
eg: var a = b = 123;
  1. 一切声明的全局变量,全是window的属性。
eg: var a = 123; ===> window.a = 123

预编译

  1. 第一步 创建A0对象 Activation Object(执行期上下文)
  2. 第二步 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
  3. 第三步 将实参值和形参统一
  4. 第四步 在函数体里面找函数声明,使赋予函数体
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);

作用域、作用域链

[[scope]]:每个jsvascript函数都是一个对象,对象中有些属性能访问,但有些不可以,这些属性仅供javascrip引擎读取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期的上下文。

作用域链: [scope]中所存贮的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。

运行期上下文: 当函数执行时,会创建一个执行上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行期上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕后,它所产生的执行上下文会被销毁。

查找变量:从作用域的顶端依次向下查找。

function a () {
  function b(){
     var  b = 234;
  }
  var a = 123;
  b();
}
var glob = 100;
a();

闭包

当内部函数被保存到外部时,将生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。

闭包作用

  • 实现公有变量
  • 可以做缓存()
  • 可以实现封装,属性私有化
  • 模块化开发,防止污染全局变量
// 实现封装 属性私有化
function Test(){
    var name = '张小慧';
    this.happy = function () {
        console.log('我的名字叫',name)
    }
}
var test = new Test();
test.happy();
test.name     // undefiend 
// 模块化开发,防止污染全局变量
var name = 'abc';
var init = (function() {
    var name = 'bcd';
     function callName() {
         console.log(name);
     } 
     return function (){
         callName();
     }
}())
init();  // bcd name变量私有化

构造函数Test中happy()方法为什么可以访问name变量?var name = '张小慧';是执行上下文中的变量,用完会被销毁。this.happy()被保存到了外部。生成了闭包。this.happy()函数有Test函数的上下文。 test.name 最后打印出来是 undefined name对于Test函数来说是私有变量。

立即执行函数

此函数没有声明,在一次执行过后即释放。适合做初始化工作。

var num = (function (a, b, c){
  var d = a + b + c;
  return d;
}(1, 2, 3))
console.log(num)
//  只有表达式才能被执行符号执行

// 函数声明
function test(){
}() // 报错 只有表达式才能被执行

// 能被执行符号执行 
var test = function() {
  console.log('a')
}()
console.log(test)  // undefuined 

对象

对象创建方法

  • var obj = {} plainObject 对象字面量/对象直接量
  • 构造函数创建方法
    • 系统自带的构造函数 var obj = new Object()
    • 自定义构造函数(大驼峰命名规则)
function Car(color){
  this.color = color;
  this.name = 'BMW';
  this.height = '1400';
  this.lang = '4900';
  this.weight = 1000;
}
var car = new Car('red');
var car1 = new Car('green');

function Car(color){
  this.color = color;
  this.name = 'BMW';
  this.height = '1400';
  this.lang = '4900';
  this.weight = 1000;
  return {};    // 返回{}
  return 123;   //忽略
}
var car = new Car('red');
var car1 = new Car('green');

构造函数内部原理

  • 在函数体最前面隐式加上this = {}
  • 执行this.xxx = xxx
  • 隐式的返回this
function Person(name, height){
  var that = {};
  that.name = name;
  that.height = height;
  return that;
}
var person = Person('zhangxiaohui', 165)

包装类

原型

  • 定义: 原型是对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
  • 利用原型的特点和概念,可以提取公共属性。
  • 对象如何查看原型 ---> 隐式属性 proto
  • 对象如何查看对象的构造函数 -->constructor
Person.prototype.name = 'abc';
function Person() {
  // var this = {
      __proto__: Person.prototype
  }
}
var obj = {
  name: 'sunny';
}
var person = new Person();
person.__proto = obj;
obj.name = 'sunny';
person.name 会先查找构造函数里面有没有name,如果this.__proto__没有,向Person.prototype里面查找

call /apply

  • 作用,改变this指向
  • 传参不同
function test(){}
test() ===> test.call()
function Person(name, age) {
    this.name = name;
    this.age = age;
}
var obj = {}
Person.call(obj, 'cheng', 300);
console.log(obj)  {name: "cheng", age: 300}
>借用别人方法实现自己功能
function Person(name, age, sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}

function Student(name, age, sex, tel, grade) {
    Person.call(this, name, age, sex);
    this.tel = tel;
    this.grade = grade;
}

var stuent = new Student('sunny', 123, 'male', 139, 2018);
console.log(stuent);

继承

  • 传统形式 原型链
    • (缺点)过多了继承了没用的属性
  • 借用构造函数
    • (缺点)不能继承借用构造函数的原型
    • (缺点)每次多调用了一个构造函数
  • 共享原型
    • 不能随便改动自己的原型
  • 圣杯模式
Father.prototype.lastName = "Deng";
function Father() {
   
}
function Son(){ 
}
function inherit(Target, Origin) {
   Target.prototype = Origin.prototype;
}
inherit(Son,Father);
var son = new Son();
console.log(son.lastName);
function inherit(Targrt, Origin){
   function F() {
   }
   F.prototype = Origin.prototype;
   Targrt.prototype = new F();  
   Targrt.prototype.constuctor = Targrt;
   Targrt.prototype.uber = Origin.prototype;
}
Father.prototype.lastName = 'Deng'
function Father(){   
}
function Son() {

}
inherit(Son, Father);
var son = new Son();
var father = new Father();
Son.prototype.name = 'huihui';
console.log(son.name)  huihui
console.log(father.name)  undefined

命名空间

  • 管理变量,防止污染全局,适用于模块化开发。
var org = {
    department1: {
        zhangxiaohui: '',
        lushasha: '',
    }
    department2: {
        nala: '',
        panpan: '',
    }
}
var zhangxiaohui = org.department1.zhangxiaohui;

对象枚举

  • for in
  var obj = {
   name: '123',
   age: 123,
   sex: 'male'
}
for(var prop in obj){
   console.log(obj.prop)   undefined * 3 
   因为 这里 obj.propv 会转换成 obj['prop']
   
}
 var obj = {
    name: '123',
    age: 123,
    sex: 'male',
    __proto__: {
        lastName: 'hui',
        
    }
}
for(var prop in obj){
    console.log(obj[prop]) // 返回原型上的东西 不会返回系统原型上的东西
}
  • hasOwnProperty
var obj = {
    name: '123',
    age: 123,
    sex: 'male',
    __proto__: {
        lastName: 'hui'
    }
}
for(var prop in obj){
    if(obj.hasOwnProperty){
        console.log(prop)    //不会返回原型链上的东西
    }
}
  • in
var obj = {
    name: '123',
    age: 123,
    sex: 'male',
    __proto__: {
        lastName: 'hui'
    }
}
console.log('name' in obj)  true
console.log('lastName' in obj)  true
  • instanceof
// 看A对象的原型链上有没有B的原型 
A instanceof B

function Person(){
}

var person = new Person();
console.log(person instanceof Person)  // true

[] instanceof Array // true

[] instanceof Object //true

判断值为数组还是对象

// 第一种方法
var array = [];
var obj = {};
console.log(array.constructor);  //ƒ Array() { [native code] }
console.log(obj.constructor)  //ƒ Object() { [native code] }

// 第二种方法
console.log(array instanceof Array)  // true
console.log(obj instanceof Array)  // false

// 第三种方法
console.log(Object.prototype.toString.call(array));  //[object Array]
console.log(Object.prototype.toString.call(obj));  //[object Object]

this

  • 函数预编译过程this -> window
  • 全局作用域 this->windoow
  • call/apply可以改变函数运行时的this指向
  • obj.function(){} (function 里面的this指向obj)
var name = '222';
var a = {
    name: '111';
    say: function() {
        console.log(this.name);
    }
}
var fun = a.say;
fun(); // 全局调用 222
a.say();   // 111

var b = {
    name: '333';
    say: function(fun){
        // this ---> b 注意这里不是 this.b
        fun();    // 没有人调用 执行方式预编译
        // 相当于输出 a 里面的console.log(this.name)
    }
}
b.say(a.say);  //222
b.say = a.say;
b.say();  // 333
var foo = 123;
function print() {
  this.foo = 234;
  console.log(foo);
}
print().   // 234

console.log(foo) print AO里面没有foo,打印的是GO里面的foo.this指向window.

var foo = 123;
function print() {
  this.foo = 234;
  console.log(foo);
}
 new print();  // 123

new print 会在 print函数里面 隐式发生 var this = Object.create(print.prototype) this 指向 print