通过代码深入理解js中几个重要的概念

1.原型

function foo(){
  console.log(x);
}
Object.prototype.x=10;
foo();   //  10

function outer(){
  var xx =20;
  (function inner(){
    console.log(xx);
  })()
}
Object.prototype.xx = 10;  //等价于outer.prototype.xx
outer();  //20

2.考察函数的属性arguments

function factorial(num) {
  if(num<=1){
    return num;
  }else{
    return num*arguments.callee(num-1);
  }
}
var another = factorial;
console.log(another(3));

function b(x,y,z){
  arguments[2] = 10;
  console.log(z);
  console.log(typeof arguments);
}
b(1,2,3);

function sum(){
  if(arguments.length == 1){
    return arguments[0] + 5;
  }else if(arguments.length == 2){
    return arguments[0] + arguments[1];
  }
}
console.log(sum(1,3));
console.log(sum(1));

3.不仅可以将函数作为一个参数传递给另一个函数,也可以将一个函数作为另一个函数的返回值:如下面的例子。

function createCompare (proto) {
  
  function createSort(obj1,obj2){
    var val1 = obj1[proto];
    var val2 = obj2[proto];
    if(val1val2){
      return 1;
    }else{return 0;}
  }
  return createSort;
}

var objs = [{'name':'css','age':12},{'name':'zz','age':22}]; 
objs.sort(createCompare('name'));
console.log(objs);
objs.sort(createCompare('age'));
console.log(objs);

4.es5中无块级作用域导致的变量提升

var tmp = 1;
function ff(){
  console.log(tmp);  //undefined,因为es5无块级作用域,导致if内的变量提升,而没有调用最外部的变量
  if(true){
    var tmp = 10;
  }
  console.log(tmp); //10
}
ff();

5.引用类型的数据和基本类型的数据在复制或者函数参数传值时候的区别

//基本数据类型的两个变量之间互不影响
var a = 10;
var b = a;
console.log([a,b]);   //[10,10]
b = 20;
console.log([a,b]);   //[10,20]
//引用类型的数据,复制给另一个变量的是一个指针,因此复制后,两个变量指向同一块内存,之间就相互影响
var obj = {'name':'cdd','age':12};
var obj1 = obj;
console.log(obj.name);    //'cdd'
obj1.name = 'zz';
obj1.job =  'teacher';
console.log(obj.name);    //'zz'
console.log(obj.job);   //'teacher'

附:es6


通过代码深入理解js中几个重要的概念_第1张图片
es6.png

6.创建对象的几种常用方法

创建单个对象的两种方法

// 创建一个Object实例,再为他添加属性
var obj = new Object();// 或者var obj = {};。。。很随意
obj.name='cdd';

//对象字面量方法
var person = {
  name:'cdd',
  age:123
};
Object.defineProperty(person,'name',{writable:false}); //这行演示一下设置对象属性的值是否可以更改。。
person.name = 'xx';
console.log(person.name); // 'cdd'

创建多个对象

//工厂模式
function createPeople(name,age){
  var o = {};  //必须先显示的创建一个对象
  o.name = name;
  o.age = age;
  return o;  //并且还要返回该对象
}
var p1 = createPeople('cdd',12);
console.log(p1);

// 构造函数模式 (构造函数的名字首字母必须大写)
function People(name,age){
  this.name = name;
  this.age = age;
}
var p2 = new People('xx',12);  // 任何一个函数,只要通过new操作符来调用,那他就可以作为构造函数
console.log(p2);

//原型模式
function Person(){}  //不必再构造函数内定义实例信息
Person.prototype.name ='cdd';  //原型prototype
Person.prototype.age = 11;
Person.prototype.say = function(){
  console.log(this.name);
};  //  或者写成Person.prototype =  name: 'cdd', age: 11, say: function(){ console.log(this.name); }

var p1 = new Person();//使用原型模式的好处是可以让所有实例对象共享它所包含的属性和方法
p1.say();

7.利用匿名函数模仿块级作用域(私有作用域)

function f(x) {
  (function(){
     for(var i = 0; i < 2; i++) {
       console.log(x);
     }
   })(); //这里还有一个问题,将匿名函数封装在一个()里面就表示将函数声明转化为函数表达式
  console.log(i);    
}
f(1);  // 1  1   error,这时候就找不到i了
 

8.变量查找规则以及函数的执行环境是在定义的时候确定的,不是调用时

function add(a) {
   var c = a + a;
   function addAA(b) { return c + b;}
   return addAA;
}
var fn = add(1);
fn(10);

然后执行var fn = add(1);fn(10);,通过上面的分析我们知道,add(1)的时候创建了一个执行环境,假设叫做Env1,其上一级执行环境是全局执行环境。在执行环境Env1中有一个变量a,在执行到var c 的时候在执行环境Env1中创建了一个叫做c的变量,然后赋值为a+a也就是2。同时也创建了一个函数addAA,其中保存了:["b"],Env1(当前执行环境), "return c + b;"。当执行这个addAA函数的时候,会创建一个新的执行环境,其上一级执行环境是Env1,然后给形参赋值,然后执行c+b。这句中使用了变量c,解释器就会查找这个变量的值,首先当前执行环境中并没有这个 c (只有变量b),所以就递归的查找到上一级执行环境。于是在add1中查找了变量c,所以这个c就是此次引用的c,其值为2。
当我们把 addAA 作为参数返回,并赋值给 fn 的时候。fn 依旧是一个保存了:["b"],Env1, return c + b;。根据上面函数的执行过程可知,所以执行 fn 和执行 addAA 没有什么区别,因为函数的执行结果仅仅与对象中保存的这三个值有关,而与函数的调用位置没有关系。

9.自执行匿名函数和立即执行的函数表达式区别

function foo() { foo(); }// 这是一个自执行的函数,函数内部执行自身,递归
var foo = function () { foo(); };// 这可能也是一个自执行的匿名函数,仅仅是foo标示名称引用它自身
var foo = function () { arguments.callee(); };// 这是一个自执行的匿名函数,因为没有标示名称,必须使用arguments.callee属性来执行自己

(function () { /* code */ } )();//这样才是立即执行的函数表达式

10.函数声明(在上下文环境中创建)与函数表达式(在代码执行阶段创建)

  function foo(){} // 声明,因为它是程序的一部分
  var bar = function foo(){}; // 表达式,因为它是赋值表达式的一部分
  new function bar(){}; // 表达式,因为它是new表达式
  (function foo(){}); // 表达式:包含在分组操作符内,将函数声明转化为函数表达式
  (function(){
       function bar(){} // 声明,因为它是函数体的一部分
  })();

11.一个执行上下文的生命周期分为创建和执行两个阶段。

创建阶段主要工作是生成变量对象、建立作用域链和确定 this 指向。
而执行阶段主要是变量赋值以及执行其它代码等。

12.隐式全局变量(比如:a = 10;)并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的.

你可能感兴趣的:(通过代码深入理解js中几个重要的概念)