Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.
Например, ниже - класс ученика, который будет использовать его в нашем коде.
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
Приведенный ниже код дает вам исключение с нулевым указателем.
public class School {
Student obj_Student;
public School() {
try {
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
Поскольку вы используете Obj_Student
, но вы забыли инициализировать его, как в правильном коде, показанном ниже:
public class School {
Student obj_Student;
public School() {
try {
obj_Student = new Student();
obj_Student.setId(12);
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
В дополнение к большим ответам до сих пор я хотел бы представить совершенно другую причину, по которой функции стрелок в определенном смысле существенно лучше, чем «обычные» функции JavaScript. Для обсуждения, давайте временно предположим, что мы используем проверку типа TypeScript или «Flow» Facebook. Рассмотрим следующий игрушечный модуль, который является действительным кодом ECMAScript 6 плюс плюс аннотации типа потока: (я буду включать нетипный код, который в конечном итоге будет получен из Babel в конце этого ответа, поэтому он может быть запущен.)
export class C {
n : number;
f1: number => number;
f2: number => number;
constructor(){
this.n = 42;
this.f1 = (x:number) => x + this.n;
this.f2 = function (x:number) { return x + this.n;};
}
}
Теперь посмотрим, что произойдет, когда мы используем класс C из другого модуля, например:
let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!
Как вы можете видеть, ошибка проверки типа здесь: f2 должен был возвратить число, но он вернул строку!
Хуже того, кажется, что нет мыслимой проверки типа может обрабатывать обычные (не стрелочные) функции JavaScript, потому что «это» из f2 не встречается в списке аргументов f2, поэтому требуется type для «this» не может быть добавлен как аннотация к f2.
Эта проблема также затрагивает людей, которые не используют контролеры типов? Я так думаю, потому что даже когда у нас нет статических типов, мы думаем, что они там. («Первые параметры должны быть числом, второе - строкой» и т. Д.). Скрытое «это» действие, которое может или не может быть использовано в теле функции, делает нашу умственную бухгалтерию более сложной.
Вот текущая нетипизированная версия, которая будет вызываться Babel:
class C {
constructor() {
this.n = 42;
this.f1 = x => x + this.n;
this.f2 = function (x) { return x + this.n; };
}
}
let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!
Функции стрелок - наиболее широко используемая функция ES6 до сих пор ...
blockquote>Использование: все функции ES5 должны быть заменены функциями стрелок ES6, за исключением следующих сценариев:
Функции стрелок НЕ должны использоваться:
- Когда мы хотим, чтобы функция hoisting как функции стрелок была анонимной.
- Когда мы хотим использовать
this
/arguments
в функции, поскольку функции стрелки не имеютthis
/arguments
, они зависят от их внешнего контекста.- Когда мы хотим использовать именованную функцию, поскольку функции стрелок анонимны.
- Когда мы хотим использовать функцию как
constructor
, поскольку функции стрелки не имеют своихthis
.- Когда мы хотим добавить функцию как свойство в объектный литерал и использовать в ней объект, поскольку мы не можем получить доступ к
this
(который должен быть самим объектом).Давайте рассмотрим некоторые из вариантов функций стрелок, которые лучше понять:
Вариант 1: Когда мы хотим передать более одного аргумента функции и вернуть некоторые
Версия ES5 :
var multiply = function (a,b) { return a*b; }; console.log(multiply(5,6)); //30
Версия ES6 :
var multiplyArrow = (a,b) => a*b; console.log(multiplyArrow(5,6)); //30
Примечание: ключевое слово
function
НЕ требуется.=>
требуется.{}
являются необязательными, когда мы не предоставляем{}
return
неявно добавляется JavaScript, и когда мы предоставляем{}
, нам нужно добавитьreturn
, если нам это нужно.Вариант 2: Если мы хотим передать ТОЛЬКО один аргумент функции и вернуть некоторое значение из него.
Версия ES5 :
var double = function(a) { return a*2; }; console.log(double(2)); //4
Версия ES6 :
var doubleArrow = a => a*2; console.log(doubleArrow(2)); //4
Примечание: при передаче только одного аргумента мы можем опустить скобки
()
.Вариант 3: когда мы НЕ хотим передавать какие-либо аргумент функции и НЕ хотите возвращать какое-либо значение.
Версия ES5 :
var sayHello = function() { console.log("Hello"); }; sayHello(); //Hello
Версия ES6 :
var sayHelloArrow = () => {console.log("sayHelloArrow");} sayHelloArrow(); //sayHelloArrow
Вариант 4: Если мы хотим явно вернуться из функций стрелок.
Версия ES6 :
var increment = x => { return x + 1; }; console.log(increment(1)); //2
Вариант 5: Когда мы хотим вернуть объект из функций стрелок.
Версия ES6 :
var returnObject = () => ({a:5}); console.log(returnObject());
Примечание: нам нужно обернуть объект в скобки
()
, в противном случае JavaScript не может различать блок и объект.Вариант 6: Arro w не имеют
arguments
(массив, такой как объект), они зависят от внешнего контекста дляarguments
.Версия ES6 :
function foo() { var abc = i => arguments[0]; console.log(abc(1)); }; foo(2); // 2
Примечание:
foo
- это функция ES5 с массивомarguments
, подобным объекту, и переданный ему аргумент2
, поэтому дляfoo
дляarguments[0]
установлено значениеarguments[0]
.
abc
- это функция стрелок ES6, так как у нее нет ееarguments
, поэтому она выводитarguments[0]
вfoo
, это внешний контекст.Вариант 7: Функции стрелки НЕ имеют
this
, они зависят от внешнего контекста дляthis
Версия ES5 :
var obj5 = { greet: "Hi, Welcome ", greetUser : function(user) { setTimeout(function(){ console.log(this.greet + ": " + user); // "this" here is undefined. }); } }; obj5.greetUser("Katty"); //undefined: Katty
Примечание. Обратный вызов, переданный setTimeout, является ES5 и имеет свой собственный
this
, который не определен в средеuse-strict
, поэтому мы получаем выход:undefined: Katty
Версия ES6 :
var obj6 = { greet: "Hi, Welcome ", greetUser : function(user) { setTimeout(() => console.log(this.greet + ": " + user)); // this here refers to outer context } }; obj6.greetUser("Katty"); //Hi, Welcome: Katty
Примечание. Обратный вызов, переданный в
setTimeout
, является функцией стрелок ES6, и у него нет своегоthis
, поэтому он берет его из внешнего контекста, который являетсяgreetUser
, который имеетthis
, который являетсяobj6
, следовательно мы получаем выход:Hi, Welcome: Katty
Разное: мы не можем использовать функцию
new
со стрелками. Функции стрелки не имеют свойстваprototype
. У нас нет привязкиthis
, когда функция стрелки вызывается черезapply
илиcall
.
Функции стрелок были созданы для упрощения функции scope
и решения ключевого слова this
, упростив ее. Они используют синтаксис =>
, который выглядит как стрелка.
Примечание: он не заменяет существующие функции. Если вы замените каждый синтаксис функций на функции со стрелками, он не будет работать во всех случаях.
Давайте посмотрим на существующий синтаксис ES5. Если ключевое слово this
находилось внутри метода объекта (функция который принадлежит объекту), к чему это относится?
var Actor = {
name: 'RajiniKanth',
getName: function() {
console.log(this.name);
}
};
Actor.getName();
Вышеприведенный фрагмент ссылается на object
и печатает имя "RajiniKanth"
. Давайте рассмотрим нижеприведенный фрагмент и посмотрим, что здесь будет здесь.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
. А теперь, если ключевое слово this
находилось внутри method’s function
?
Здесь это будет означать window object
, чем inner function
, как его выпавшее из scope
. Поскольку this
всегда ссылается на владельца функции, в которой он находится, для этого случая - поскольку он теперь выходит из области видимости - объект window / global.
Когда он находится внутри object
's метод - владелец function
является объектом. Таким образом, это ключевое слово привязано к объекту. Однако, когда он находится внутри функции, как автономной, так и внутри другого метода, она всегда будет ссылаться на объект window/global
.
var fn = function(){
alert(this);
}
fn(); // [object Window]
Есть способы решить эту проблему в нашем ES5
сам, рассмотрим, что перед тем, как погрузиться в функции ES6 arrow, как решить проблему.
Как правило, вы должны создать переменную вне внутренней функции метода. Теперь метод ‘forEach’
получает доступ к this
и, следовательно, к свойствам object’s
и их значениям.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
var _this = this;
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
, используя bind
, чтобы прикрепить ключевое слово this
, которое ссылается на метод method’s inner function
.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
}).bind(this);
}
};
Actor.showMovies();
Теперь с помощью функции стрелки ES6
мы можем более эффективно решить проблему lexical scoping
.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach((movie) => {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
Arrow functions
больше похожи на операторы функций, за исключением того, что они bind
это parent scope
. Если аргумент arrow function is in top scope
, this
будет ссылаться на window/global scope
, тогда как функция стрелки внутри регулярной функции будет иметь свой этот аргумент так же, как и внешнюю функцию.
С функциями arrow
this
привязан к закрытию scope
во время создания и не может быть изменен. Новый оператор, bind, call и apply не влияют на это.
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`
asyncFunction(o, function (param) {
// We made a mistake of thinking `this` is
// the instance of `o`.
console.log('param === this?', param === this);
});
}
};
o.doSomething(); // param === this? false
В приведенном выше примере мы потеряли контроль над этим. Мы можем решить приведенный выше пример, используя переменную ссылку this
или используя bind
. С ES6 становится легче управлять this
в качестве привязки к lexical scoping
.
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`.
//
// Because this arrow function is created within
// the scope of `doSomething` it is bound to this
// lexical scope.
asyncFunction(o, (param) => {
console.log('param === this?', param === this);
});
}
};
o.doSomething(); // param === this? true
Внутри литерала объекта.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
getName: () => {
alert(this.name);
}
};
Actor.getName();
Actor.getName
определяется функцией стрелки, но при вызове он оповещает о неопределенности, поскольку this.name
является undefined
, поскольку контекст остается в window
.
Это происходит потому, что функция стрелки связывает контекст лексически с внешним окружением window object
... i.e. Выполнение this.name
эквивалентно window.name
, которое не определено.
Прототип объекта
Это же правило применяется при определении методов на prototype object
. Вместо использования функции-стрелки для определения метода sayCatName, который приводит к некорректному context window
:
function Actor(name) {
this.name = name;
}
Actor.prototype.getName = () => {
console.log(this === window); // => true
return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined
Конструкторы Invoke
this
в вызове построения - это вновь созданный объект , При выполнении нового Fn () контекст constructor Fn
является новым объектом: this instanceof Fn === true
.
this
настроен из охватывающего контекста, то есть внешней области, которая делает ее не назначенной вновь созданному объекту.
var Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');
Обратный вызов с динамическим контекстом
Функция Arrow связывает декларацию context
статически и не может сделать ее динамической. Присоединение прослушивателей событий к элементам DOM является общей задачей при программировании на стороне клиента. Событие запускает функцию обработчика с этим в качестве целевого элемента.
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
this
- это окно в функции стрелки, которая определена в глобальном контексте. Когда происходит событие клика, браузер пытается вызвать функцию обработчика с контекстом кнопки, но функция стрелки не изменяет свой предварительно определенный контекст. this.innerHTML
эквивалентен window.innerHTML
и не имеет смысла.
Вы должны применить выражение функции, которое позволяет изменить это в зависимости от целевого элемента:
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
Когда пользователь нажимает кнопку, это в функции обработчика является кнопкой. Таким образом, this.innerHTML = 'Clicked button'
корректно изменяет текст кнопки, чтобы отразить статус щелчка.
Ссылки: https://rainsoft.io/when-not-to-use-arrow-functions-in-javascript/
Простым способом,
var a =20; function a(){this.a=10; console.log(a);}
//20, since the context here is window.
Другой экземпляр:
var a = 20;
function ex(){
this.a = 10;
function inner(){
console.log(this.a); //can you guess the output of this line.
}
inner();
}
var test = new ex();
Ans: Консоль будет печатать 20.
Причина, функция выполняет свой собственный стек, в этом примере функция ex
выполняется с оператором new
, поэтому будет создан контекст, и когда inner
будет выполнен, JS создаст новый стек и выполнит inner
, но существует локальный контекст.
Итак, если мы хотим, чтобы функция inner
имела локальный контекст, который является ex
, нам нужно привязать контекст к внутренней функции .
Стрелки решают эту проблему, вместо того, чтобы принимать Global context
, они берут local context
, если они существуют. В given example,
он примет значение new ex()
как this
.
Итак, во всех случаях, когда привязка является явной, Arrows решает проблему по умолчанию.
Я предпочитаю использовать функции стрелок во все времена, когда не требуется доступ к локальному this
, потому что функция стрелки не связывает свои собственные, аргументы, супер или new.target .